GraphQL返回接口类型时出现Abstract类型解析报错,寻求解决方案
GraphQL返回接口类型时出现Abstract类型解析报错,寻求解决方案
嘿,我仔细看了你的问题和代码,这个报错其实是GraphQL处理抽象类型(接口/联合类型)的必经环节——当你返回User这种接口类型时,GraphQL必须明确知道运行时拿到的具体对象是哪个实现类型(是AdminUser还是AppUser),不然它没法正确解析字段和做类型校验。
你现在用buildSchema这种SDL字符串定义schema的方式虽然便捷,但没办法直接给接口添加resolveType函数,也没法给具体类型加isTypeOf函数,这就是报错的核心原因。下面给你两种可行的解决方案:
方案一:改用代码优先方式构造Schema
放弃buildSchema,改用graphql包提供的类型构造函数手动定义schema,这样就能给接口添加类型解析逻辑:
import express from "express"; import { graphqlHTTP } from "express-graphql"; import { GraphQLSchema, GraphQLObjectType, GraphQLInterfaceType, GraphQLID, GraphQLString, GraphQLInt, GraphQLBoolean, GraphQLEnumType } from "graphql"; const UserStatuses = Object.freeze({ "registered": "REGISTERED", "active": "ACTIVE", "inactive": "INACTIVE" }); const users = [ // 你的用户数据和原代码一致,这里省略... ]; // 定义UserStatus枚举类型 const UserStatusEnum = new GraphQLEnumType({ name: 'UserStatus', values: { REGISTERED: { value: UserStatuses.registered }, ACTIVE: { value: UserStatuses.active }, INACTIVE: { value: UserStatuses.inactive } } }); // 定义User接口 const UserInterface = new GraphQLInterfaceType({ name: 'User', fields: { id: { type: GraphQLID }, first_name: { type: GraphQLString }, last_name: { type: GraphQLString }, email: { type: GraphQLString } }, // 关键:添加resolveType函数判断具体类型 resolveType: (user) => { // 根据数据特征区分:AdminUser有is_admin字段,AppUser有age字段 return user.is_admin ? 'AdminUser' : 'AppUser'; } }); // 定义AdminUser具体类型 const AdminUserType = new GraphQLObjectType({ name: 'AdminUser', interfaces: [UserInterface], fields: { id: { type: GraphQLID }, first_name: { type: GraphQLString }, last_name: { type: GraphQLString }, email: { type: GraphQLString }, is_admin: { type: GraphQLBoolean }, status: { type: UserStatusEnum } } }); // 定义AppUser具体类型 const AppUserType = new GraphQLObjectType({ name: 'AppUser', interfaces: [UserInterface], fields: { id: { type: GraphQLID }, first_name: { type: GraphQLString }, last_name: { type: GraphQLString }, email: { type: GraphQLString }, age: { type: GraphQLInt }, status: { type: UserStatusEnum } } }); // 定义Query类型 const QueryType = new GraphQLObjectType({ name: 'Query', fields: { user: { type: UserInterface, args: { id: { type: GraphQLInt } }, resolve: (_, { id }) => users.find(u => u.id === id) } } }); // 创建最终schema const schema = new GraphQLSchema({ query: QueryType }); // 启动服务部分和原代码一致 var app = express(); app.get('/', (req, res) => res.send("Hello World!")); app.use('/graphql', graphqlHTTP({ schema, rootValue: {}, // 这里resolver已经定义在QueryType里了,不需要rootValue pretty: true, graphiql: true })); app.listen(4000, () => console.log('Graphql Server is browse to http://localhost:4000/graphql'));
方案二:手动给返回对象添加__typename字段
如果你不想重构schema定义方式,也可以在resolver返回数据时,手动加上__typename字段,直接告诉GraphQL对象的具体类型:
修改你的root resolver部分:
var root = { user: function ({id}) { let user = users.find(u => u.id == id); if (!user) return null; // 根据用户特征添加对应的__typename const typename = user.is_admin ? 'AdminUser' : 'AppUser'; return { ...user, __typename: typename }; }, }
这样GraphQL就能直接识别出具体类型,不会再抛出那个抽象类型解析的报错了。
两种方案对比
- 方案一更适合复杂项目,类型逻辑集中管理,后续扩展新的User实现类型时,只需要在
resolveType里加判断即可,维护性更好。 - 方案二更轻量化,适合快速修复现有代码,不需要大改schema结构,但后续新增类型时要记得同步更新resolver里的
__typename判断逻辑。
你可以根据自己的项目情况选一个试试,应该就能解决问题啦!
备注:内容来源于stack exchange,提问作者Sachin Kumar
相关产品推荐
相关产品推荐

