Apollo 订阅未在客户端监听新数据

Posted

技术标签:

【中文标题】Apollo 订阅未在客户端监听新数据【英文标题】:Apollo Subscriptions not listening to new data on client 【发布时间】:2019-05-03 21:19:59 【问题描述】:

以前我的 apollo 设置一直在监听订阅,直到我添加到 socket.io 中,现在我的客户端设置不再监听新数据。根据我使用 graphql 游乐场的测试,我的服务器代码似乎没问题。

在我的浏览器控制台中,我收到以下错误消息

client.js:652 WebSocket 连接到“ws://localhost:4000/”失败:WebSocket 握手期间出错:意外响应代码:400

我的客户端设置似乎存在一些问题,以使用 apollo 订阅。

欣赏任何指针?提前致谢

import  ApolloClient  from "apollo-client";
import  onError  from "apollo-link-error";
import  ApolloLink, split  from "apollo-link";
import  createUploadLink  from "apollo-upload-client";
import gql from "graphql-tag";
import  withClientState  from "apollo-link-state";
import  InMemoryCache  from "apollo-cache-inmemory";
import  WebSocketLink  from "apollo-link-ws";
import  getMainDefinition  from "apollo-utilities";
import  setContext  from "apollo-link-context";

    const cache = new InMemoryCache();

    const defaultState = 
      currentGame: 
        __typename: "currentGame",
        teamAScore: 0,
        teamBScore: 0,
        teamAName: "EAGLES",
        teamBName: "LOL"
      
    ;

    const stateLink = withClientState(
      cache,
      defaults: defaultState,
      resolvers: 
        Mutation: 
          updateGame: (_,  index, value ,  cache ) => 
            const query = gql`
              query GetCurrentGame 
                currentGame @client 
                  teamAScore
                  teamBScore
                  teamAName
                  teamBName
                
              
            `;
            const previous = cache.readQuery( query );
            const data = 
              currentGame: 
                ...previous.currentGame,
                [index]: value
              
            ;
            cache.writeQuery( query, data );
            return null;
          ,
          resetCurrentGame: (_, d,  cache ) => 
            cache.writeData( data: defaultState );
          
        
      
    );

    const host = "http://localhost:4000";

    // httpLink
    const httpLink = createUploadLink(
      uri: `$host/graphql`,
      credentials: "same-origin"
    );

    // wsLink
    const wsLink = new WebSocketLink(
      uri: `ws://localhost:4000/`,
      options: 
        reconnect: true
      
    );

    // using the ability to split links, you can send data to each link
    // depending on what kind of operation is being sent
    const webLink = split(
      // split based on operation type
      ( query ) => 
        const  kind, operation  = getMainDefinition(query);
        return kind === "OperationDefinition" && operation === "subscription";
      ,
      wsLink,
      httpLink
    );

    // authMiddleware
    const authLink = setContext(async (req,  headers ) => 
      //  const token = await AsyncStorage.getItem("@token");
      const token = "";

      return 
        headers: 
          ...headers,
          authorization: token ? `$token` : ""
        
      ;
    );

    const errorLink = onError(( networkError, graphQLErrors ) => 
      if (graphQLErrors) 
        graphQLErrors.map(( message, locations, path ) =>
          console.log(
            `[GraphQL error]: Message: $message, Location: $locations, Path: $path`
          )
        );
      
      if (networkError) console.log(`[Network error]: $networkError`);
    );

    export const client = new ApolloClient(
      link: ApolloLink.from([authLink, stateLink, errorLink, webLink]),
      cache
    );

我的服务器端代码(如果需要)

//! Using Apollo Server Express
const app = express();
const path = "/graphql";
const schema = genSchema();

export const startServer = async () => 
  const server = new ApolloServer(
    schema,
    context: ( req : any) => (
      req,
      pubsub,
      userLoader: userLoader()
    )
  );

  app.use(cors());

  app.use(authMiddleware);

  app.use("/images", express.static("images"));

  app.use(
    "graphql",
    graphqlUploadExpress(
      uploadDir: "/",
      maxFileSize: 100000000,
      maxFiles: 10
    ),
    graphqlHTTP( schema ) as any
  );

  server.applyMiddleware( app, path );

  //! Added Subscription Handler
  const httpServer = createServer(app);
  server.installSubscriptionHandlers(httpServer);

  const port = process.env.PORT || 4000;

  await createConnection();

  await httpServer.listen(
    port
  );

  console.log(`Server is running on localhost:$port$server.graphqlPath`);
;

【问题讨论】:

【参考方案1】:

已纠正。我的客户端 apollo 设置应该指向 ws://localhost:4000/graphql 而不仅仅是 ws://localhost:4000/

// wsLink
const wsLink = new WebSocketLink(
  uri: `ws://localhost:4000/graphql`,
  options: 
    reconnect: true
  
);

【讨论】:

以上是关于Apollo 订阅未在客户端监听新数据的主要内容,如果未能解决你的问题,请参考以下文章

突变后使用订阅和更新创建重复节点 - 使用 Apollo 客户端

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

Apollo 发布和订阅错误:无法查询字段?

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

如何在 GraphQL Apollo 客户端中处理异步突变

Apollo 客户端订阅必须提供模式