Apollo GraphQL Server订阅:如何仅发送客户端关注的变更数据?
Apollo Server 订阅仅推送变更字段方案
要实现只在客户端关注的字段发生变化时才推送更新,且不用手动处理字段解析或获取订阅信息,可以通过以下方式实现:
利用Apollo Server插件拦截订阅结果
Apollo Server的插件系统提供了订阅生命周期钩子,其中didResolveSubscription可以在解析出要发送给客户端的结果后、实际推送前介入。你可以在这个钩子中,为每个WebSocket连接维护上次发送结果的缓存,对比当前结果与缓存值,只有当两者不同时才允许推送。示例代码如下:
const { ApolloServer } = require('@apollo/server'); const { isEqual } = require('lodash'); // 用深对比替代JSON.stringify,更可靠 const server = new ApolloServer({ typeDefs, resolvers, plugins: [ { async didResolveSubscription(context, payload) { // 从连接上下文获取缓存的上次结果 const cache = context.connectionContext.cache; const connectionId = context.connectionContext.connectionId; const lastPayload = cache.get(connectionId); // 深对比当前结果与上次结果 if (lastPayload && isEqual(payload, lastPayload)) { // 无变化则返回null,终止推送 return null; } // 更新缓存为当前结果 cache.set(connectionId, payload); return payload; }, }, ], context: ({ connection }) => { // 为每个WebSocket连接初始化专属缓存 if (connection) { return { connectionContext: { connectionId: connection.id, // 用Apollo自动生成的连接ID标识 cache: new Map(), }, }; } return {}; }, });自定义订阅执行流程
如果你需要更灵活的控制,可以自定义订阅的执行逻辑。在订阅解析器中,每次生成数据源的更新后,先让Apollo根据客户端的订阅字段解析出最终响应,再与该客户端的历史响应对比。这种方式依赖Apollo的info对象完成字段筛选,你只需要负责对比结果。示例代码如下:
const resolvers = { Subscription: { dataUpdate: { async subscribe(parent, args, context, info) { // 假设你的数据源是一个AsyncIterator const iterator = yourDataSource.getUpdates(); return { async next() { const { value, done } = await iterator.next(); if (done) return { done }; // 让Apollo根据客户端的订阅字段解析结果 const resolvedValue = await info.schema.execute({ schema: info.schema, document: info.document, rootValue: value, contextValue: context, variableValues: info.variableValues, operationName: info.operationName, }); // 对比缓存的上次结果 const lastValue = context.connectionContext.cache.get(context.connectionContext.connectionId); if (lastValue && isEqual(resolvedValue.data, lastValue)) { // 无变化则跳过本次,继续获取下一个更新 return this.next(); } context.connectionContext.cache.set(context.connectionContext.connectionId, resolvedValue.data); return { value: resolvedValue.data, done: false }; }, async return() { return iterator.return(); }, async throw(error) { return iterator.throw(error); }, [Symbol.asyncIterator]() { return this; }, }; }, }, }, };
这两种方案的核心都是让Apollo完成客户端订阅字段的自动筛选,你只需要对比最终要发送给客户端的结果是否与上次一致,避免重复实现字段解析逻辑。
内容的提问来源于stack exchange,提问作者engelant
相关产品推荐
相关产品推荐

