如何在GraphQL Schema中避免Type与Input类型的代码重复?
相信你也发现了,手动重复定义Agreement和AgreementInput这种字段完全一致的类型,不仅写起来麻烦,后期维护也容易因为漏改一处导致不一致。其实有几种可行的方案来解决这个问题,具体要看你用的技术栈:
1. 借助后端框架的类型复用能力(比如NestJS、TypeGraphQL)
如果你用的是支持类装饰器的Node.js框架,比如NestJS GraphQL或者TypeGraphQL,可以通过类继承来复用字段定义:
以NestJS为例,你只需要定义一次核心字段,然后让Input类型继承Object类型即可,框架会自动处理类型适配:
import { ObjectType, Field, InputType, Int } from '@nestjs/graphql'; // 定义基础的Object Type @ObjectType() export class Agreement { @Field(() => Int) id: number; @Field() agmtCategCd: string; @Field() agmtSubcategyCd: string; } // 继承Agreement类生成Input Type @InputType() export class AgreementInput extends Agreement {}
TypeGraphQL的实现逻辑类似,通过@ObjectType()和@InputType()装饰器配合继承,就能自动复用字段,不用重复写。
2. 使用GraphQL代码生成工具
如果是纯SDL(Schema Definition Language)开发,或者不想依赖框架的特性,可以用GraphQL Code Generator这类工具自动生成重复的类型。
你只需要在配置文件中指定要生成Input类型的Object Type,工具会帮你自动导出对应的Input类型,比如在codegen.yml中配置:
schema: ./src/schema.graphql generates: ./src/generated/graphql.ts: plugins: - typescript - typescript-resolvers - typescript-graphql-files-modules config: # 配置自动生成Input类型 inputTypeSuffix: Input # 其他配置...
这样只要你定义了Agreement类型,工具就会自动生成AgreementInput,字段完全同步,后期修改原类型时,重新生成即可。
3. 用模板引擎预生成SDL
如果不想用代码生成工具,还可以用模板引擎(比如Handlebars、EJS)来写SDL模板,只定义一次字段,然后分别生成Type和Input类型:
比如写一个Handlebars模板type-template.hbs:
// 生成Object Type type {{typeName}} { {{#each fields}} {{name}}: {{type}} {{/each}} } // 生成对应的Input Type input {{typeName}}Input { {{#each fields}} {{name}}: {{type}} {{/each}} }
然后传入参数(比如typeName: "Agreement",fields: [{name: "id", type: "Int"}, ...]),就能自动生成对应的SDL代码,避免手动重复。
为什么GraphQL本身不支持直接继承?
顺便提一句,GraphQL的SDL本身并没有提供让Input Type继承Object Type的语法——因为在GraphQL的类型系统设计里,Object Type用于输出数据(比如查询返回的结果),Input Type用于接收输入参数,二者属于不同的类型层级,所以官方没有提供直接的继承语法,需要借助工具或框架来实现复用。
内容的提问来源于stack exchange,提问作者Mohamed Taboubi

