[英]How to use Apollo GraphQL subscriptions in the client-side NextJS?
我是 NextJS 的新手。 我有一個頁面需要顯示從 Hasura GraphQL 后端提取的實時數據。
在其他非 NextJS 應用程序中,我將 GraphQL 訂閱與 Apollo 客戶端庫一起使用。 在引擎蓋下,這使用了 websockets。
當不使用訂閱時,我可以讓 GraphQL 在 NextJS 中工作。 我很確定這是在服務器端運行的:
import React from "react";
import { AppProps } from "next/app";
import withApollo from 'next-with-apollo';
import { ApolloProvider } from '@apollo/react-hooks';
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import { getToken } from "../util/auth";
interface Props extends AppProps {
apollo: any
}
const App: React.FC<Props> = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps}/>
</ApolloProvider>
);
export default withApollo(({ initialState }) => new ApolloClient({
uri: "https://my_hasura_instance.com/v1/graphql",
cache: new InMemoryCache().restore(initialState || {}),
request: (operation: any) => {
const token = getToken();
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : ''
}
});
}
}))(App);
我這樣使用它:
import { useQuery } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const myQuery = gql`
query {
...
}
`;
const MyComponent: React.FC = () => {
const { data } = useQuery(myQuery);
return <p>{JSON.stringify(data)}</p>
}
但是,我想這樣做:
import { useSubscription } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const myQuery = gql`
subscription {
...
}
`;
const MyComponent: React.FC = () => {
const { data } = useSubscription(myQuery);
return <p>{JSON.stringify(data)}</p>
}
我試過的
我嘗試在 ApolloClient 中拆分 HttpLink 和 WebsocketLink 元素,如下所示:
import React from "react";
import { AppProps } from "next/app";
import { ApolloProvider } from '@apollo/react-hooks';
import withApollo from 'next-with-apollo';
import { InMemoryCache } from "apollo-cache-inmemory";
import ApolloClient from "apollo-client";
import { split } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { getToken } from "../util/auth";
interface Props extends AppProps {
apollo: any
}
const App: React.FC<Props> = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps}/>
</ApolloProvider>
);
const wsLink = new WebSocketLink({
uri: "wss://my_hasura_instance.com/v1/graphql",
options: {
reconnect: true,
timeout: 10000,
connectionParams: () => ({
headers: {
authorization: getToken() ? `Bearer ${getToken()}` : ""
}
})
},
});
const httpLink = new HttpLink({
uri: "https://hasura-g3uc.onrender.com/v1/graphql",
});
const link = process.browser ? split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink
) : httpLink;
export default withApollo(({ initialState }) => new ApolloClient({
link: link,
cache: new InMemoryCache().restore(initialState || {}),
}))(App);
但是當我加載頁面時,我得到一個Internal Server Error
,並且在終端中出現這個錯誤:
Error: Unable to find native implementation, or alternative implementation for WebSocket!
在我看來, ApolloClient
是在服務器端生成的,沒有 WebSocket 實現。 我怎樣才能在客戶端發生這種情況?
找到解決方法使其工作,看看這個答案https://github.com/apollographql/subscriptions-transport-ws/issues/333#issuecomment-359261024
原因是服務器端渲染; 這些語句必須在瀏覽器中運行,所以我們測試我們是否有process.browser
!
隨附 github 鏈接中的相關部分:
const wsLink = process.browser ? new WebSocketLink({ // if you instantiate in the server, the error will be thrown
uri: `ws://localhost:4000/subscriptions`,
options: {
reconnect: true
}
}) : null;
const httplink = new HttpLink({
uri: 'http://localhost:3000/graphql',
credentials: 'same-origin'
});
const link = process.browser ? split( //only create the split in the browser
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httplink,
) : httplink;
這個答案似乎更實際
https://github.com/apollographql/subscriptions-transport-ws/issues/333#issuecomment-775578327
您應該通過npm i ws
安裝ws
並將webSocketImpl: ws
添加到 WebSocketLink 參數。
import ws from 'ws';
const wsLink = new WebSocketLink({
uri: endpoints.ws,
options: {
reconnect: true,
connectionParams: () => ({
...getToken() && {Authorization: getToken()}
})
},
webSocketImpl: ws
});
解決方案:將 wsLink 設為 function 變量,如下面的代碼。
// src/apollo.ts
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql'
});
const wsLink = () => {
return new GraphQLWsLink(createClient({
url: 'ws://localhost:3000/graphql'
}));
}
export const apolloClient = new ApolloClient({
link: typeof window === 'undefined' ? httpLink : wsLink(),
cache: new InMemoryCache(),
});
// pages/_app.tsx
import { ApolloProvider } from "@apollo/client";
import { apolloClient } from "./apollo";
function MyApp({ Component, pageProps }) {
return (
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.