在GraphQL订阅中无法将PubSub传入Context的问题求助
问题描述
在mongodb-express-graphql-nextjs项目中实现Subscriptions功能时,Query和Mutation的Resolver均可正常获取Context,但订阅Resolver内的Context打印为undefined,无法读取context.pubsub属性,报错信息:cannot read property 'pubsub' in 'context'。
相关Resolver代码(comment.gql.js):
export const resolvers = { Subscription: { commentAdded: { subscribe: (_, args, context) => { console.log(context) // 输出Undefined return context.pubsub.asyncIterator('NEW_COMMENT') // 报错:无法读取context的pubsub属性 }, }, }, Query: { // 其他操作正常工作 }, Mutation: { // 其他操作正常工作 } }
已做配置
- Apollo Server已创建PubSub实例,并在
expressMiddleware的context中传入pubsub - 配置了WebSocketServer用于订阅
- Apollo Client(Next.js)已配置
GraphQLWsLink用于订阅连接 - 通过
makeExecutableSchema合并了各模块的typeDef和resolvers
解决方案
问题核心是HTTP请求的Context配置与WebSocket订阅的Context配置相互独立,你仅在expressMiddleware中传入了pubsub,但WebSocket连接的Context未做配置,导致订阅Resolver无法获取到上下文数据。
1. 配置WebSocketServer的Context
在创建并配置WebSocketServer时,需单独指定context函数,将pubsub传入:
import { WebSocketServer } from 'ws'; import { useServer } from 'graphql-ws/lib/use/ws'; import { schema } from './path/to/your/schema'; import { PubSub } from 'graphql-subscriptions'; // 全局PubSub实例(确保和expressMiddleware中使用的是同一个) const pubsub = new PubSub(); // 绑定到现有Express服务器的WebSocket服务 const wsServer = new WebSocketServer({ server: yourExpressHttpServer, path: '/graphql/subscriptions', // 订阅请求路径 }); // 配置graphql-ws服务,指定订阅专用Context useServer( { schema, context: () => ({ pubsub }), // 这里传入pubsub到订阅上下文 }, wsServer );
2. 核对HTTP请求的Context配置
确保expressMiddleware中的Context配置正常(这部分你已完成,可做二次验证):
import { ApolloServer } from '@apollo/server'; import { expressMiddleware } from '@apollo/server/express4'; const server = new ApolloServer({ schema }); await server.start(); app.use( '/graphql', express.json(), expressMiddleware(server, { context: async ({ req }) => ({ pubsub, // HTTP请求上下文传入pubsub // 其他需要的上下文数据,如用户信息等 }), }) );
3. 验证修复效果
重启服务器后重新测试订阅功能,此时订阅Resolver中的context应能正常获取到pubsub实例,调用context.pubsub.asyncIterator('NEW_COMMENT')不再报错。
关键说明
Apollo Server中,Query/Mutation对应的HTTP请求上下文由expressMiddleware的context函数生成,而订阅对应的WebSocket连接上下文由graphql-ws的useServer配置中的context函数生成,二者完全独立,必须分别配置才能保证所有Resolver都能获取到所需的上下文数据。
内容的提问来源于stack exchange,提问作者ton1

