You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Next.js中集成Apollo Server Subscriptions遇到的问题

Next.js + Apollo Server WebSocket订阅故障排查与修复

问题概述

在Next.js中基于Apollo Server实现WebSocket订阅服务,使用apollo-server-micro包后仍无法正常运行,端点诊断显示存在CORS配置缺失问题,WebSocket连接与订阅功能失效。

核心问题与修复方案

1. CORS配置缺失

端点诊断结果显示OPTIONS和POST请求缺少必要的CORS响应头,这会导致客户端(包括Apollo Studio)无法正常发起请求,WebSocket连接也会受影响。

修复代码:修改src/pages/api/graphql.ts中的OPTIONS请求处理逻辑:

if (req.method === 'OPTIONS') {
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  res.setHeader('Access-Control-Allow-Origin', '*'); // 生产环境请替换为具体允许的域名
  res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
  res.setHeader(
    'Access-Control-Allow-Headers',
    'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
  );
  return res.end();
}

2. WebSocket上下文与PubSub挂载错误

  • WebSocket服务的上下文错误依赖HTTP请求的req.socket,而非graphql-ws提供的上下文参数
  • PubSub实例未正确挂载到服务器对象,导致订阅解析器无法获取有效PubSub实例

修复代码:

  1. 在src/pages/api/graphql.ts中导入PubSub实例并挂载到服务器:
import { pubsub } from '@/server/pubSubInstanciate'; // 导入你的PubSub实例

// ...

if (!res.socket.server.apolloServer) {
  // 挂载PubSub到服务器对象
  if (!res.socket.server.pubsub) {
    res.socket.server.pubsub = pubsub;
  }

  res.socket.server.apolloServer = apolloServer;

  if (!graphqlWSS) {
    console.log(`Initializing GraphQL WSS :>>>>>>>>>>>>>>`);
    const wss = new WebSocketServer({
      server: res.socket.server,
      path: '/api/graphql',
    });

    graphqlWSS = useServer(
      {
        schema,
        context(ctx) {
          // 从graphql-ws上下文获取服务器对象
          return { pubsub: ctx.socket.server.pubsub, prisma };
        },
      },
      wss
    );
    // 将WebSocket服务实例挂载到服务器,方便热重载清理
    res.socket.server.graphqlWSS = graphqlWSS;
  }

  await startServer;
  apolloServerHandler = apolloServer.createHandler({ path: '/api/graphql' });
}
  1. 修正Apollo Server的HTTP上下文:
const apolloServer = new ApolloServer({
  schema,
  cache: 'bounded',
  context({ req }) {
    return { pubsub: req.socket?.server?.pubsub, prisma };
  },
  // ...插件配置
});

3. 客户端WebSocket链接硬编码

客户端硬编码ws://localhost:3000会导致生产环境或不同域名下连接失败,需动态生成WebSocket URL。

修复代码:修改src/pages/_app.tsx中的WebSocket链接配置:

const wsLink =
  typeof window !== 'undefined'
    ? new GraphQLWsLink(
        createClient({
          // 根据当前页面协议和域名动态生成WebSocket地址
          url: `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/api/graphql`,
        })
      )
    : null;

4. 订阅解析器Resolve函数错误

订阅解析器的resolve函数返回值错误,未返回实际的订阅数据。

修复代码:修改src/server/graphql/resolvers/chatMessageResolver.ts中的订阅resolve函数:

Subscription: {
  chatMessageCreated: {
    subscribe: (_: undefined, _args: undefined, ctx: any) =>
      pubsub.asyncIterator(['CHAT_MESSAGE_CREATED']),
    resolve: (payload) => {
      // 返回订阅的实际数据
      return payload.chatMessageCreated;
    },
  },
},

5. 热重载时WebSocket服务未清理

当前热重载逻辑未清理旧的WebSocket服务,可能导致连接冲突。

修复代码:修改src/pages/api/graphql.ts中的热重载处理逻辑:

const oldApolloServer = res.socket.server.apolloServer;

if (oldApolloServer && oldApolloServer !== apolloServer) {
  console.warn('Fixing Apollo Server hot reload');
  oldApolloServer.stop();
  // 清理旧的WebSocket服务
  if (res.socket.server.graphqlWSS) {
    await res.socket.server.graphqlWSS.dispose();
    delete res.socket.server.graphqlWSS;
  }
  delete res.socket.server.apolloServer;
}

验证步骤

  1. 重启Next.js开发服务器
  2. 使用Apollo Studio测试订阅功能,或在客户端发起订阅请求
  3. 重新运行端点诊断命令npx diagnose-endpoint@1.1.0 --endpoint=http://localhost:3000/api/graphql,确认CORS问题已解决

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 13:16:17