Graphql

联合类型和枚举

学习如何在 NestJS GraphQL 中定义和使用联合类型和枚举,包括代码优先和模式优先两种方法。

联合类型和枚举

联合类型

联合类型与接口非常相似,但它们不指定类型之间的任何公共字段(了解更多请参阅这里)。联合类型对于返回不相关对象类型非常有用。

代码优先

要定义 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 函数注册 Result 联合:

import { createUnionType } from '@nestjs/graphql';

export const ResultUnion = createUnionType({
  name: 'Result',
  types: () => [Author, Book],
});

现在,在我们的解析器中注册 ResultUnion

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

这将在 SDL 中生成以下 GraphQL 模式部分:

type Author {
  name: String!
}

type Book {
  title: String!
}

union Result = Author | Book

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

要提供自定义的 resolveType() 函数,请将 resolveType 属性添加到传入 createUnionType() 函数的选项对象中,如下所示:

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

模式优先

要在模式优先方法中定义联合,只需使用 SDL 创建一个 GraphQL 联合。

type Author {
  name: String!
}

type Book {
  title: String!
}

union Result = Author | Book

然后,您可以使用类型生成功能(如快速开始章节所示)来生成相应的 TypeScript 定义:

export class Author {
  name: string;
}

export class Book {
  title: string;
}

export type Result = Author | Book;

联合需要在解析器映射中有一个额外的 __resolveType 字段来确定联合应该解析为哪种类型(这与接口相同)。另外,请注意,Result 联合必须在 GraphQLModule 配置中的 resolvers 映射中定义:

export const resolvers = {
  Result: {
    __resolveType(value) {
      if (value.name) {
        return 'Author';
      }
      if (value.title) {
        return 'Book';
      }
      return null;
    },
  },
};

枚举

枚举类型是一种特殊的标量,限制为一组特定的允许值(了解更多请参阅这里)。这允许您:

  1. 验证此类型的任何参数都是允许值之一
  2. 通过类型系统传达字段将始终是有限值集合之一

代码优先

使用代码优先方法时,您可以通过创建一个 TypeScript 枚举并用 registerEnumType 函数装饰它来定义 GraphQL 枚举类型。

import { registerEnumType } from '@nestjs/graphql';

export enum AllowedColor {
  RED,
  GREEN,
  BLUE,
}

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

registerEnumType 函数从 @nestjs/graphql 包中导出。

现在您可以在我们的类型中引用 AllowedColor 枚举:

import { Field, InputType } from '@nestjs/graphql';
import { AllowedColor } from './allowed-color.enum';

@InputType()
export class CreateCatInput {
  @Field()
  name: string;

  @Field(() => AllowedColor)
  color: AllowedColor;
}

这将在 SDL 中生成以下 GraphQL 模式部分:

enum AllowedColor {
  RED
  GREEN
  BLUE
}

要为枚举提供描述,请将 description 属性传递给 registerEnumType() 函数。

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

要弃用枚举值或为其提供描述,请传递 valuesMap 属性:

registerEnumType(AllowedColor, {
  name: 'AllowedColor',
  description: '支持的猫咪颜色。',
  valuesMap: {
    RED: {
      description: '红色。',
    },
    GREEN: {
      description: '绿色。',
      deprecationReason: '不再支持',
    },
  },
});

这将在 SDL 中生成以下 GraphQL 模式部分:

"""
支持的猫咪颜色。
"""
enum AllowedColor {
  """
  红色。
  """
  RED

  """
  绿色。
  """
  GREEN @deprecated(reason: "不再支持")
  BLUE
}

模式优先

要在模式优先方法中定义枚举,只需使用 SDL 创建一个 GraphQL 枚举。

enum AllowedColor {
  RED
  GREEN
  BLUE
}

然后,您可以使用类型生成功能(如快速开始章节所示)来生成相应的 TypeScript 定义:

export enum AllowedColor {
  RED,
  GREEN,
  BLUE,
}

有时后端内部使用的值与公共 API 中的值不同。在这个例子中,API 包含 REDGREENBLUE,而在解析器中我们可能使用 #f00#0f0#00f。要解决这个问题,请在解析器映射中声明一个解析器对象:

export const resolvers = {
  AllowedColor: {
    RED: '#f00',
    GREEN: '#0f0',
    BLUE: '#00f',
  },
};