如何在客户端 NextJS 中使用 Apollo GraphQL 订阅?

Posted

技术标签:

【中文标题】如何在客户端 NextJS 中使用 Apollo GraphQL 订阅?【英文标题】:How to use Apollo GraphQL subscriptions in the client-side NextJS? 【发布时间】:2020-08-27 18:54:48 【问题描述】:

我是 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 实现。如何在客户端实现这一点?

【问题讨论】:

我也有同样的问题... 【参考方案1】:

找到解决方法让它工作,看看这个答案https://github.com/apollographql/subscriptions-transport-ws/issues/333#issuecomment-359261024

【讨论】:

【参考方案2】:

这个答案似乎更实际

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
);

【讨论】:

以上是关于如何在客户端 NextJS 中使用 Apollo GraphQL 订阅?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Apollo 客户端在 nextJs 中传递授权标头的最佳方法? ReferenceError: localStorage 未定义

作为道具传递与提取缓存 Apollo 客户端 Nextjs

在 nextJS 上配置 apollo 客户端以进行订阅的问题

如何在 NextJS 应用程序中从 Apollo 链接上下文访问 Express HTTP 请求?

Apollo 客户端未在 Nextjs Lamda 中发送 JWT 不记名令牌

NextJS + Apollo:如何在 getServerSideProps 内的 apolloClient.query 上获取多个查询?