NestJS Logo
NestJS 中文文档
v10.0.0
  • 介绍
  • 快速上手
  • 控制器
  • 提供者
  • 模块
  • 中间件
  • 异常过滤器
  • 管道
  • 守卫
  • 拦截器
  • 自定义装饰器
  • 自定义提供者
  • 异步提供者
  • 动态模块
  • 依赖注入作用域
  • 循环依赖
  • 模块引用
  • 懒加载模块
  • 执行上下文
  • 生命周期事件
  • 发现服务
  • 跨平台无关性
  • 测试
迁移指南
API 参考
官方课程
  1. 文档
  2. GraphQL 支持
  3. 联合类型和枚举

接口
字段中间件

联合类型(Union)

联合类型与接口(Interface)非常相似,但它们之间没有任何共同字段(详细说明可参考这里)。联合类型适用于需要从单个字段返回多种不相交数据类型的场景。

代码优先(Code first)

要定义一个 GraphQL 联合类型,我们需要先定义该联合类型所包含的各个类。参考 Apollo 官方文档中的示例,我们将创建两个类。首先是 Book:

import { Field, ObjectType } from '@nestjs/graphql'

@ObjectType()
export class Book {
  @Field()
  title: string
}

接下来是 Author:

import { Field, ObjectType } from '@nestjs/graphql'

@ObjectType()
export class Author {
  @Field()
  name: string
}

完成上述定义后,我们可以通过 @nestjs/graphql 包中导出的 createUnionType 函数注册 ResultUnion 联合类型:

export const ResultUnion = createUnionType({
  name: 'ResultUnion',
  types: () => [Author, Book] as const,
})
注意

通过 createUnionType 函数的 types 属性返回的数组应加上 const 断言。如果没有加 const 断言,编译时会生成错误的声明文件,并且在其他项目中使用时会报错。

现在,我们可以在查询中引用 ResultUnion:

@Query(() => [ResultUnion])
search(): Array<typeof ResultUnion> {
  return [new Author(), new Book()]
}

这样会在生成的 GraphQL SDL 中得到如下片段:

type Author {
  name: String!
}

type Book {
  title: String!
}

union ResultUnion = Author | Book

type Query {
  search: [ResultUnion!]!
}

库默认生成的 resolveType() 函数会根据解析器方法返回值自动提取类型。这意味着必须返回类实例,而不能返回普通的 JavaScript 字面量对象。

如果需要自定义 resolveType() 函数,可以在传递给 createUnionType() 的选项对象中添加 resolveType 属性,如下所示:

export const ResultUnion = createUnionType({
  name: 'ResultUnion',
  types: () => [Author, Book] as const,
  resolveType(value) {
    if (value.name) {
      return Author
    }
    if (value.title) {
      return Book
    }
    return null
  },
})

模式优先(Schema first)

在模式优先方式中,定义联合类型非常简单,只需使用 SDL 创建一个 GraphQL 联合类型即可。

type Author {
  name: String!
}

type Book {
  title: String!
}

union ResultUnion = Author | Book

接下来,你可以使用类型定义自动生成功能(详见快速入门章节),自动生成对应的 TypeScript 类型定义:

export class Author {
  name: string
}

export class Book {
  title: string
}

export type ResultUnion = Author | Book

在 GraphQL 中,联合类型解析时需要在解析器映射(resolver map)中额外实现一个 __resolveType 字段,用于确定当前联合类型实际应解析为哪种类型。同时,需要注意,ResultUnionResolver 类必须作为提供者注册到某个模块中。下面我们来创建一个 ResultUnionResolver 类,并定义 __resolveType 方法。

@Resolver('ResultUnion')
export class ResultUnionResolver {
  @ResolveField()
  __resolveType(value) {
    if (value.name) {
      return 'Author'
    }
    if (value.title) {
      return 'Book'
    }
    return null
  }
}
提示
所有装饰器均从 @nestjs/graphql 包中导出。

枚举类型(Enums)

枚举类型是一种特殊的标量类型,其值被限定为一组特定的可选值(详细内容可参考这里)。这样做可以:

  • 校验该类型的参数是否属于允许的值之一
  • 通过类型系统明确告知:某个字段的值始终属于有限集合中的某一项

代码优先(Code first)

在代码优先的方式下,你只需创建一个 TypeScript 枚举,即可定义一个 GraphQL 枚举类型。

export enum AllowedColor {
  RED,
  GREEN,
  BLUE,
}

完成上述定义后,使用 @nestjs/graphql 包中导出的 registerEnumType 函数注册 AllowedColor 枚举:

registerEnumType(AllowedColor, {
  name: 'AllowedColor',
})

现在,你可以在类型定义中引用 AllowedColor:

@Field(type => AllowedColor)
favoriteColor: AllowedColor

这样会在生成的 GraphQL 架构 SDL 中得到如下内容:

enum AllowedColor {
  RED
  GREEN
  BLUE
}

如需为枚举类型添加描述,可以在 registerEnumType() 函数中传入 description 属性:

registerEnumType(AllowedColor, {
  name: 'AllowedColor',
  description: '支持的颜色。',
})

如需为枚举值添加描述,或标记某个值为已弃用,可以传入 valuesMap 属性,如下所示:

registerEnumType(AllowedColor, {
  name: 'AllowedColor',
  description: '支持的颜色。',
  valuesMap: {
    RED: {
      description: '默认颜色。',
    },
    BLUE: {
      deprecationReason: '颜色过于蓝。',
    },
  },
})

这将在生成的 GraphQL 架构 SDL 中得到如下内容:

"""
支持的颜色。
"""
enum AllowedColor {
  """
  默认颜色。
  """
  RED
  GREEN
  BLUE @deprecated(reason: "颜色过于蓝。")
}

架构优先(Schema first)

在架构优先方式下,只需在 SDL 中定义 GraphQL 枚举类型:

enum AllowedColor {
  RED
  GREEN
  BLUE
}

然后,你可以使用类型定义自动生成工具(具体用法见快速入门章节)生成对应的 TypeScript 定义:

export enum AllowedColor {
  RED
  GREEN
  BLUE
}

有时,后端内部枚举值与 API 公共接口中的值不同。例如,API 中为 RED,但在解析器中实际使用 #f00(详细说明见这里)。要实现这一点,可以为 AllowedColor 枚举声明一个解析器对象:

export const allowedColorResolver: Record<keyof typeof AllowedColor, any> = {
  RED: '#f00',
}
提示

所有装饰器均从 @nestjs/graphql 包中导出。

然后,将该解析器对象与 GraphQLModule#forRoot() 方法的 resolvers 属性一起使用,如下所示:

GraphQLModule.forRoot({
  resolvers: {
    AllowedColor: allowedColorResolver,
  },
})