Apollo 2.x:Subscription Resolver未触发问题排查
我记得你2017年那篇关于Apollo订阅解析器未激活的帖子!没想到升级到2.x又碰到类似的坑,旧解法还不管用,这几天折腾下来肯定够闹心的。结合Apollo 2.x的版本变化,给你列几个重点排查方向:
1. 核对Schema中订阅类型的定义
Apollo 2.x对Schema的语法校验更严格,必须明确定义完整的Subscription类型,不能像1.x那样有宽松的写法。比如:
// 正确的写法 type Subscription { newEvent: Event! // 确保字段类型和返回逻辑匹配 } // 如果是扩展现有类型,要使用extend关键字 extend type Subscription { updatedItem: Item! }
如果订阅字段没有归到Subscription类型下,解析器根本不会被识别。
2. 检查ApolloServer的订阅配置
2.x里订阅的启用方式和1.x完全不同,必须在创建ApolloServer实例时显式配置subscriptions选项,否则WebSocket服务不会启动,解析器自然无法激活:
const { ApolloServer } = require('apollo-server'); const server = new ApolloServer({ typeDefs, resolvers, // 必须配置这部分 subscriptions: { path: '/graphql/subscriptions', // 和客户端连接的路径要一致 onConnect: (connectionParams, webSocket) => { console.log('客户端已连接订阅服务'); // 这里可以做权限校验等操作 }, }, });
3. 确认Resolver的结构是否合规
2.x要求订阅Resolver必须放在顶层的Subscription对象下,每个字段的subscribe方法必须返回AsyncIterator(通常用pubsub.asyncIterator包装),别和Query/Mutation的Resolver混在一起:
const resolvers = { Subscription: { newEvent: { // 订阅触发逻辑 subscribe: (_, args, { pubsub }) => pubsub.asyncIterator('NEW_EVENT_TOPIC'), // 可选:对返回的 payload 做处理 resolve: (payload) => payload.newEvent, }, }, // 其他Query/Mutation Resolver... };
如果Resolver层级错了,Apollo Server找不到对应的订阅处理逻辑。
4. 验证PubSub实例的全局一致性
如果你的PubSub是在每次请求中新建的,订阅和发布会处于不同的消息通道,导致解析器收不到触发信号。必须确保整个应用使用同一个PubSub实例:
// 全局初始化一次(建议放在单独的模块里) const { PubSub } = require('apollo-server'); const pubsub = new PubSub(); module.exports = pubsub; // 在Resolver和发布逻辑中引用这个全局实例 const pubsub = require('./pubsub'); pubsub.publish('NEW_EVENT_TOPIC', { newEvent: eventData });
生产环境不建议用默认的内存PubSub,换成RedisPubSub之类的分布式实现更可靠。
5. 排查客户端的订阅连接
客户端也要对应升级到2.x的订阅逻辑,确保WebSocket连接路径和服务端配置一致,并且初始化时没有报错:
import { SubscriptionClient } from 'subscriptions-transport-ws'; const subscriptionClient = new SubscriptionClient( 'ws://localhost:4000/graphql/subscriptions', // 和服务端path一致 { reconnect: true } );
如果客户端连不上WebSocket,订阅请求根本传不到服务端的解析器。
先从这几个方向排查,应该能找到问题所在!
内容的提问来源于stack exchange,提问作者VikR

