Apollo Server:如何向嵌套解析器传递参数
针对你遇到的这个嵌套解析器获取父字段参数的问题,我给你几个实用的解决方案,都不需要修改你的查询结构:
方案一:通过父解析器返回值携带参数
这是最直接的方法——在property解析器里,把需要传递的someArgs和原本的业务数据一起返回,后续的嵌套解析器通过parent参数就能拿到这个值。
需要注意的是,我们可以用一个不会和业务字段冲突的键名(比如__parentArgs)来存储参数,而且这个额外字段不会被返回给客户端,因为你的GraphQL类型定义里并没有定义它,只会在解析器链中传递。
示例代码:
// property 解析器 property: async (_, { someArgs }) => { // 获取原本的property业务数据 const propertyData = await yourDataSource.getProperty(someArgs); // 返回包装后的对象,携带需要传递的参数 return { ...propertyData, __parentArgs: someArgs, }; } // nestedField 解析器 nestedField: async (parent) => { // 从parent中取出传递的someArgs const someArgs = parent.__parentArgs; // 用someArgs获取对应的nestedField数据 return yourDataSource.getNestedField(parent.id, someArgs); } // moreNestedField 解析器同理 moreNestedField: async (parent) => { const someArgs = parent.__parentArgs; return yourDataSource.getMoreNestedField(parent.id, someArgs); }
方案二:使用WeakMap隔离上下文参数
如果你不想污染返回的业务数据,可以借助context里的WeakMap来存储每个父对象对应的参数。WeakMap的特性是键为对象,且不会阻止对象被垃圾回收,多个property查询之间也不会互相干扰,因为每个propertyData都是独立的对象。
示例代码:
// 创建Apollo Server时初始化上下文 const server = new ApolloServer({ typeDefs, resolvers, context: () => ({ // 用WeakMap存储父对象与参数的映射 parentArgsMap: new WeakMap(), }), }); // property 解析器 property: async (_, { someArgs }, ctx) => { const propertyData = await yourDataSource.getProperty(someArgs); // 将当前property数据和对应的someArgs关联 ctx.parentArgsMap.set(propertyData, someArgs); return propertyData; } // nestedField 解析器 nestedField: async (parent, _, ctx) => { // 通过parent对象取出对应的someArgs const someArgs = ctx.parentArgsMap.get(parent); return yourDataSource.getNestedField(parent.id, someArgs); }
方案三:遍历AST获取父字段参数(进阶)
如果上面两种方法都不适合你,还可以通过解析GraphQL的AST(抽象语法树)来找到父级property字段的参数。这个方法需要用到GraphQL的工具函数,相对复杂一些,适合特殊场景使用。
示例代码:
const { getArgumentValues } = require('graphql/execution/values'); const { find } = require('lodash'); // 也可以自己实现查找逻辑 // nestedField 解析器 nestedField: async (parent, _, __, info) => { // 从path向上遍历,找到parent为property的节点 let currentPath = info.path.prev; let targetFieldNode = null; while (currentPath) { if (currentPath.key === 'property') { // 在fieldNodes中找到对应的字段节点 targetFieldNode = find(info.fieldNodes, node => node.name.value === 'property'); break; } currentPath = currentPath.prev; } if (!targetFieldNode) { throw new Error('找不到父级property字段'); } // 解析字段的参数 const queryType = info.schema.getType('Query'); const propertyField = queryType.getFields().property; const args = getArgumentValues( propertyField, targetFieldNode, info.variableValues || {} ); const someArgs = args.someArgs; return yourDataSource.getNestedField(parent.id, someArgs); }
总结
- 优先推荐方案一,实现简单直观,对现有代码改动小;
- 如果不想污染业务数据,选择方案二,利用WeakMap做隔离,更优雅;
- 方案三适合复杂场景,但代码量较大,维护成本高。
内容的提问来源于stack exchange,提问作者Tdy
相关产品推荐
相关产品推荐

