
后端GraphQLAPI设计【免费下载链接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!项目地址https://gitcode.com/gh_mirrors/ty/type-graphql点击查看免费下载导读本文基于 TypeGraphQL v0.17.0 官方文档《Inheritance》系统讲解 TypeGraphQL 中两大继承能力——类型继承Types Inheritance与Resolver 类继承Resolvers Inheritance。读完本文你将掌握通过继承复用ArgsType()/InputType()/ObjectType()类型定义以贯彻 DRY 原则通过工厂函数配合Resolver({ isAbstract: true })搭建可复用的基础 CRUD Resolver并用name选项生成唯一查询/变更名同时理解父类方法覆盖的规则与常见tsconfig编译陷阱。背景TypeGraphQL 的类与装饰器建模TypeGraphQL 的核心思想是用 TypeScript 类 装饰器来定义 GraphQL Schema——ObjectType()描述输出对象、InputType()描述输入对象、ArgsType()描述参数集合Resolver()描述解析器。既然类的组合与复用是面向对象编程的基本功TypeGraphQL 自然允许通过**类的继承extends**来组合类型定义与解析器行为从而避免重复代码DRYDont Repeat Yourself。Types Inheritance类型继承场景复用分页参数在 GraphQL API 中skip与take这类分页参数几乎是所有列表查询的标配。与其在每个 Resolver 参数类中重复声明不如先声明一次ArgsType() class PaginationArgs { Field(type Int) skip: number 0; Field(type Int) take: number 25; }然后在任意参数类中通过extends复用ArgsType() class GetTodosArgs extends PaginationArgs { Field() onlyCompleted: boolean false; }这样GetTodosArgs既拥有skip、take又新增了onlyCompletedSchema 中会完整展开父类字段。适用类型与对象/输入类型同样的技术同样适用于input type 类InputType()和object type 类ObjectType()ObjectType() class Person { Field() age: number; } ObjectType() class Student extends Person { Field() universityName: string; }关键约束父子必须使用同一种类型装饰器父类与子类必须使用同类型的装饰器——上面的Person - Student都使用了ObjectType()。混用是不允许的例如子类用ObjectType()、父类用InputType()会导致 Schema 构建错误。这是因为 TypeGraphQL 在生成 Schema 时依赖元数据装饰器收集的字段信息、类类型标记来归属字段与校验一致性装饰器类型不一致意味着同一组字段被登记到了不同的类型系统中Schema 生成器无法处理这种冲突。从源码角度看类元数据与字段元数据存储在同一个 MetadataStorage 中参见 metadata-storage.tsSchema 生成阶段schema-generator.ts会按类类型分别生成 Input/Object 定义混用装饰器必然破坏这一对应关系。与接口继承的组合除了类的继承TypeGraphQL 还支持接口InterfaceType()与类继承的组合使用。仓库中的 interfaces-inheritance 示例 展示了这种更进阶的用法Person是接口Student与Employee是分别继承该接口的ObjectType()类MultiResolverresolver.ts中的persons()查询返回IPerson[]由 GraphQL 根据实际对象类型动态解析。注意当某个实现了接口的类型未直接在 Schema 中出现时需要在buildSchema中通过orphanedTypes显式注册见 index.ts。Resolvers InheritanceResolver 类继承类型继承解决的是“字段复用”而Resolver 类继承解决的是“行为复用”——例如为每个实体/资源创建一个基础 CRUD Resolver从而省去重复的样板代码。为什么需要工厂函数TypeGraphQL 要求 Schema 中的查询/变更名称全局唯一。如果直接定义一个具名的基础 Resolver 类子类继承后所有实体都会生成同名的查询导致冲突。因此需要工厂函数在工厂内部动态创建基础类并利用参数生成唯一名称。第一步定义抽象基础类工厂function createBaseResolver() { abstract class BaseResolver {} return BaseResolver; }tsconfig 编译陷阱当tsconfig.json中启用了declarations: true生成.d.ts声明文件时可能报错[ts] Return type of exported function has or is using private name BaseResolver。解决方案有两种将返回类型声明为any或者单独定义一个类/接口来描述该类的方法与属性供导出函数签名引用。第二步让工厂接收类型与名称后缀参数为了让工厂生成唯一的查询/变更名并让 Schema 知道返回类型需要传入suffix名称后缀与对象类型类function createBaseResolverT extends ClassType(suffix: string, objectTypeCls: T) { abstract class BaseResolver {} return BaseResolver; }这里用到了 TypeGraphQL 提供的ClassType工具类型见 ClassType.ts它描述了一个“类构造器”的类型约束。第三步标记Resolver({ isAbstract: true })非常重要必须用Resolver装饰器标记BaseResolver并传入isAbstract: true选项。否则多个继承该基础类的子 Resolver 会因注册同名查询/变更而抛出错误function createBaseResolverT extends ClassType(suffix: string, objectTypeCls: T) { Resolver({ isAbstract: true }) abstract class BaseResolver {} return BaseResolver; }isAbstract的作用是告诉 Schema 生成器这个 Resolver 类本身不直接产生 Schema 字段只是作为其他 Resolver 的父类模板从而避免重复注册。从源码看Resolver 元数据resolver-metadata.ts记录了isAbstract标记Schema 生成器会据此跳过抽象类的字段生成。第四步实现可复用的查询/变更方法在基础类中实现方法的方式与普通 Resolver 完全一致唯一区别是可以通过name装饰器选项覆盖最终写入 Schema 的名称function createBaseResolverT extends ClassType(suffix: string, objectTypeCls: T) { Resolver({ isAbstract: true }) abstract class BaseResolver { protected items: T[] []; Query(type [objectTypeCls], { name: getAll${suffix} }) async getAll(Arg(first, type Int) first: number): PromiseT[] { return this.items.slice(0, first); } } return BaseResolver; }这里getAll${suffix}为每个资源生成形如getAllPerson、getAllRecipe的查询名。name选项同样适用于Mutation与Subscription。第五步创建具体 Resolver 并继承const PersonBaseResolver createBaseResolver(person, Person); Resolver(of Person) export class PersonResolver extends PersonBaseResolver { // ... }子类中还能继续添加专属的查询与变更像普通 Resolver 一样使用const PersonBaseResolver createBaseResolver(person, Person); Resolver(of Person) export class PersonResolver extends PersonBaseResolver { Mutation() addPerson(Arg(input) personInput: PersonInput): Person { this.items.push(personInput); return personInput; } }最后只需在buildSchema中正常注册PersonResolver即可继承的基础 Resolver 会自动生效const schema await buildSchema({ resolvers: [PersonResolver] });覆盖父类方法的规则若想在子类中覆盖父类声明的查询/变更/订阅必须让 Schema 名称保持一致通过name选项或类方法名。覆盖会同时替换掉 GraphQL 参数与返回类型。注意仅仅在子类中提供同名但不同 Schema 名称的方法例如新增一个getOne方法不会覆盖父类实现——因为 Schema 中会同时存在两个不同名字的字段父类字段仍然保留。仓库中的完整进阶示例resolvers-inheritance 示例 展示了生产级写法。其基础工厂 resource.resolver.ts 相比文档示例更进一步通过ResourceCls.name.toLocaleLowerCase()动态生成资源名如person/recipe再拼接出person、persons、recipe、recipes等查询名用FieldResolver({ name: uuid })为所有子资源动态附加uuid字段结合typedi容器Service()与构造器注入ResourceServiceFactory实现依赖注入。子类只需一行继承即可获得完整 CRUD 能力Resolver() Service() export class PersonResolver extends ResourceResolver(Person, persons) { Mutation() promote(Arg(personId, _type Int) personId: number): boolean { const person this.resourceService.getOne(personId); // ... } }见 person.resolver.ts。生成的 Schema 中查询persons(skip: Int! 0, take: Int! 10)与person(id: Int!)均来自继承的基础类可对照 schema.graphql 验证。版本差异提醒本仓库中的 docs/inheritance.md 与 version-0.17.0 文档 略有差异新版文档的基础类示例使用了Resolver()不带isAbstract。在 v0.17.0 版本上务必加上isAbstract: true否则多个子 Resolver 会因同名查询/变更注册而报错这是 0.17.0 文档中的关键提醒。小结继承类型装饰器要求用途核心要点Types Inheritance父子同类装饰器同为ObjectType()/InputType()/ArgsType()复用字段定义如分页参数混用装饰器会引发 Schema 构建错误Resolvers Inheritance父类Resolver({ isAbstract: true })复用查询/变更/订阅实现如基础 CRUD工厂函数 name选项生成唯一名称覆盖需同名同参两种继承机制都贯彻了 DRY 原则类型继承消除字段冗余Resolver 继承消除逻辑冗余。理解它们之后你可以用极少量的代码为多个实体搭建风格统一的完整 GraphQL API这正是 TypeGraphQL 面向对象建模能力的核心价值所在。赞分享后端GraphQLAPI设计【免费下载链接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!项目地址https://gitcode.com/gh_mirrors/ty/type-graphql点击查看免费下载相关推荐DBeaver驱动包终极解决方案一键搞定30数据库连接配置DBeaver驱动包终极解决方案一键搞定30数据库连接配置 还在为DBeaver连接数据库时的驱动下载烦恼吗DBeaver驱动包为你提供了一站式离线解决方后端GraphQLAPI设计TypeGraphQL 继承机制完全指南类型继承与 Resolver 类继承的实战应用TypeGraphQL 继承机制完全指南类型继承与 Resolver 类继承的实战应用 导读 本文基于 TypeGraphQL 官方文档 website/v后端GraphQLAPI设计TypeGraphQL 继承机制实战用 TypeScript 类继承组合 GraphQL 类型与 ResolverTypeGraphQL 继承机制实战用 TypeScript 类继承组合 GraphQL 类型与 Resolver TypeGraphQL 的核心设计理念是后端GraphQLAPI设计创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考