You need to enable JavaScript to run this app. 按照Apollo文档配置后GraphQL Subscriptions连接失败如何解决?
优惠活动
大模型
产品
解决方案
定价
更多

按照Apollo文档配置后GraphQL Subscriptions连接失败如何解决?

问题排查及解决方法
    1. 服务端订阅服务未正确挂载
      Apollo Server v3及以上版本已不再默认自动挂载WebSocket订阅服务,原v2版本的写法不再生效,需要手动将订阅处理器挂载到HTTP server实例上。
    1. WebSocket连接上下文未正确处理鉴权
      你当前使用的express鉴权中间件仅能处理HTTP请求,WebSocket连接的鉴权逻辑需要单独处理。你当前的context函数直接读取req.user,订阅请求触发时req为undefined,会导致上下文异常,进而引发连接断开。
    1. 客户端与服务端WebSocket协议不兼容
      如果你的@apollo/client版本在v3.5.0以上,默认的WebSocketLink使用的是graphql-ws协议,而服务端默认适配的是subscriptions-transport-ws协议,两者不兼容会导致连接失败,需要两端使用统一的协议依赖。
    1. 空密钥导致鉴权失败
      你当前代码中的SECRET和SECRET2均为空值,JWT验证逻辑会直接抛出异常,导致请求被拦截,需要替换为实际使用的密钥值。

服务端修正后核心代码

import { createServer } from 'http'; // 新增引入
// 其余原有引入保持不变

const app = express();
const httpServer = createServer(app); // 新增创建HTTP server实例

// 原有cors、addUser中间件逻辑保持不变

const server = new ApolloServer({
   typeDefs, 
   resolvers,
   subscriptions: {
     path: '/subscriptions',
     cors: { origin: "*" } // 新增订阅跨域配置
   },
   context: ({req, res, connection}) => { 
     // 处理WebSocket连接的上下文
     if (connection) {
       return connection.context;
     }
     // 原有HTTP请求上下文逻辑
     const user = req.user;
     return { models, SECRET, SECRET2, user }; 
    },
});

server.applyMiddleware({ app });
server.installSubscriptionHandlers(httpServer); // 新增挂载订阅处理器

// 替换原有app.listen为httpServer.listen
models.sequelize.sync({force: true}).then(x => {
  httpServer.listen({ port: PORT }, () => {
    console.log(`🚀 Server ready at http://localhost:${PORT}${server.graphqlPath}`);
    console.log(`🚀 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`);
  });
});

可选补充:WebSocket鉴权逻辑

如果需要对WebSocket连接做鉴权,可以在ApolloServer的subscriptions配置中添加onConnect钩子:

subscriptions: {
  path: '/subscriptions',
  cors: { origin: "*" },
  onConnect: (connectionParams, webSocket) => {
    // 从连接参数中提取token
    const token = connectionParams.x_token || connectionParams.authorization?.replace('Bearer ', '');
    if (!token) throw new Error('未授权');
    // 验证token并返回用户信息,会自动注入到connection.context中
    const { user } = jwt.verify(token, SECRET);
    return { user };
  }
}

对应客户端需要在WebSocketLink配置中携带token:

const wsLink = new WebSocketLink({
  uri: 'ws://localhost:4000/subscriptions',
  options: {
    reconnect: true,
    // 新增连接参数携带token
    connectionParams: () => {
      const token = localStorage.getItem('token');
      return { x_token: token };
    }
  }
});

内容的提问来源于stack exchange,提问作者milanf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 12:15:02