在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实例
修复代码:
- 在
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' }); }
- 修正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; }
验证步骤
- 重启Next.js开发服务器
- 使用Apollo Studio测试订阅功能,或在客户端发起订阅请求
- 重新运行端点诊断命令
npx diagnose-endpoint@1.1.0 --endpoint=http://localhost:3000/api/graphql,确认CORS问题已解决
内容的提问来源于stack exchange,提问作者Wone
相关产品推荐
相关产品推荐

