GraphQL中能否为实现同一接口的类型定义共享字段解析器?
问题描述
我有若干本质上继承自基类型的GraphQL类型,由于GraphQL不直接支持继承,我用接口ObjectI定义基础字段,让BaseObject、ObjectA、ObjectB实现该接口,以此实现共享片段的复用。但我希望能直接在接口上定义共享字段解析器(比如relationalField的解析器),不用在每个实现类型里重复编写。目前尝试在ObjectI下定义解析器无效,只能在三个类型中重复定义同一解析器。我知道可以通过函数变量复用逻辑,但想了解GraphQL本身是否支持通过层级结构定义共享解析器?
对应的Schema定义:
union Object = BaseObject | ObjectA | ObjectB interface ObjectI { id: ID! relationalField: SomeOtherObject! ... } type BaseObject implements ObjectI { id: ID! relationalField: SomeOtherObject! ... } type ObjectA implements ObjectI { id: ID! relationalField: SomeOtherObject! ... # 额外字段 uniqueA: String! } type ObjectB implements ObjectI { id: ID! relationalField: SomeOtherObject! ... # 额外字段 uniqueB: String! }
尝试的无效解析器写法:
export const resolvers = { Query: { objects: async (parent, args, context, info) => dataSource.get(), }, Object: { __resolveType: (obj) => defineType(obj), }, ObjectI: { // 此处定义的解析器不会生效 relationalField: async (parent, args, context, info) => dataSource.getSomeOtherObject(parent), }, BaseObject: { }, ObjectA: { }, ObjectB: { }, }
当前只能采用的重复写法:
BaseObject: { relationalField: async (parent, args, context, info) => dataSource.getSomeOtherObject(parent), }, ObjectA: { relationalField: async (parent, args, context, info) => dataSource.getSomeOtherObject(parent), }, ObjectB: { relationalField: async (parent, args, context, info) => dataSource.getSomeOtherObject(parent), },
解决方案
核心结论
GraphQL规范本身不支持直接在接口上定义字段解析器并自动让实现类型继承。执行器在处理字段时,会优先匹配对象的具体类型(如BaseObject),不会自动回退到接口的解析器定义,所以直接在ObjectI下写的解析器无法生效。
可行的复用方案
虽然规范不支持,但可以通过以下方式避免重复代码:
- 抽离共享函数变量
把重复的解析逻辑抽成单独的函数,在各个类型解析器中引用即可:
// 抽离共享解析逻辑 const resolveRelationalField = async (parent, args, context, info) => { return dataSource.getSomeOtherObject(parent); }; export const resolvers = { Query: { objects: async (parent, args, context, info) => dataSource.get(), }, Object: { __resolveType: (obj) => defineType(obj), }, BaseObject: { relationalField: resolveRelationalField }, ObjectA: { relationalField: resolveRelationalField }, ObjectB: { relationalField: resolveRelationalField }, };
- 批量挂载解析器
如果实现接口的类型较多,可以写一个简单工具函数批量给这些类型挂载共享解析器:
// 定义接口的共享解析器 const objectIResolvers = { relationalField: async (parent, args, context, info) => dataSource.getSomeOtherObject(parent), }; // 列出所有实现ObjectI的类型 const objectITypes = ['BaseObject', 'ObjectA', 'ObjectB']; // 批量生成类型解析器 const typeResolvers = objectITypes.reduce((acc, typeName) => { acc[typeName] = { ...objectIResolvers }; return acc; }, {}); export const resolvers = { Query: { objects: async (parent, args, context, info) => dataSource.get(), }, Object: { __resolveType: (obj) => defineType(obj), }, ...typeResolvers, };
内容的提问来源于stack exchange,提问作者Galen Howlett
相关产品推荐
相关产品推荐

