Graphql

CLI 插件

学习如何在 NestJS GraphQL 中使用 CLI 插件来自动生成装饰器,减少样板代码,提高开发效率。

CLI 插件

警告 本章节仅适用于代码优先方法。

TypeScript 的元数据反射系统有几个限制,使得无法确定类由哪些属性组成,或者识别给定属性是可选的还是必需的。然而,其中一些约束可以在编译时解决。Nest 提供了一个插件,增强 TypeScript 编译过程,以减少所需的样板代码量。

提示 此插件是可选的。如果您愿意,可以手动声明所有装饰器,或者只在需要的地方声明特定的装饰器。

概述

GraphQL 插件将自动:

  • 使用 @Field 注释所有输入对象、对象类型和参数类属性,除非使用了 @HideField
  • 根据问号设置 nullable 属性(例如,name?: string 将设置 nullable: true
  • 根据类型设置 type 属性(也支持数组)
  • 基于注释生成属性描述(如果 introspectComments 设置为 true

请注意,您的文件名必须具有以下后缀之一,插件才能分析它们:['.input.ts', '.args.ts', '.entity.ts', '.model.ts'](例如,author.entity.ts)。如果您使用不同的后缀,可以通过指定 typeFileNameSuffix 选项来调整插件的行为(见下文)。

根据我们到目前为止学到的知识,您必须重复大量代码来让包知道您的类型应该如何在 GraphQL 中声明。例如,您可以定义一个简单的 Author 类,如下所示:

@@filename(authors/models/author.model)
@ObjectType()
export class Author {
  @Field(type => ID)
  id: number;

  @Field({ nullable: true })
  firstName?: string;

  @Field({ nullable: true })
  lastName?: string;

  @Field(type => [Post])
  posts: Post[];
}

虽然在中等规模的项目中这不是一个重大问题,但一旦您有大量的类,它就会变得冗长且难以维护。

通过启用 GraphQL 插件,上述类定义可以简单地声明为:

@@filename(authors/models/author.model)
@ObjectType()
export class Author {
  @Field(type => ID)
  id: number;
  firstName?: string;
  lastName?: string;
  posts: Post[];
}

插件基于抽象语法树动态添加适当的装饰器。因此,您不必为散布在代码中的 @Field 装饰器而烦恼。

提示 插件将自动生成任何缺失的 GraphQL 属性,但如果您需要覆盖它们,只需通过 @Field() 显式设置它们。

注释内省

启用注释内省功能后,CLI 插件将基于注释生成字段描述。

例如,给定一个示例 roles 属性:

/**
 * A list of user's roles
 */
@Field(() => [String], {
  description: `A list of user's roles`
})
roles: string[];

您必须重复描述值。启用 introspectComments 后,CLI 插件可以提取这些注释并自动为属性提供描述。现在,上述字段可以简单地声明如下:

/**
 * A list of user's roles
 */
roles: string[];

使用 CLI 插件

要启用插件,请打开 nest-cli.json(如果您使用 Nest CLI)并添加以下 plugins 配置:

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": ["@nestjs/graphql"]
  }
}

您可以使用 options 属性来自定义插件的行为。

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": [
      {
        "name": "@nestjs/graphql",
        "options": {
          "typeFileNameSuffix": [".input.ts", ".args.ts"],
          "introspectComments": true
        }
      }
    ]
  }

}

options 属性必须满足以下接口:

export interface PluginOptions {
  typeFileNameSuffix?: string[];
  introspectComments?: boolean;
}
选项默认值描述
typeFileNameSuffix['.input.ts', '.args.ts', '.entity.ts', '.model.ts']GraphQL 类型文件后缀
introspectCommentsfalse如果设置为 true,插件将基于注释生成属性描述

如果您不使用 CLI 而是有自定义的 webpack 配置,您可以将此插件与 ts-loader 结合使用:

getCustomTransformers: (program: any) => ({
  before: [require('@nestjs/graphql/plugin').before({}, program)]
}),

SWC builder

For standard setups (non-monorepo), to use CLI Plugins with the SWC builder, you need to enable type checking, as described here.

$ nest start -b swc --type-check

For monorepo setups, follow the instructions here.

$ npx ts-node src/generate-metadata.ts
# OR npx ts-node apps/{YOUR_APP}/src/generate-metadata.ts

Now, the serialized metadata file must be loaded by the GraphQLModule method, as shown below:

import metadata from './metadata'; // <-- file auto-generated by the "PluginMetadataGenerator"

GraphQLModule.forRoot<...>({
  ..., // other options
  metadata,
}),

Integration with ts-jest (e2e tests)

When running e2e tests with this plugin enabled, you may run into issues with compiling schema. For example, one of the most common errors is:

Object type <name> must define one or more fields.

This happens because jest configuration does not import @nestjs/graphql/plugin plugin anywhere.

To fix this, create the following file in your e2e tests directory:

const transformer = require('@nestjs/graphql/plugin');

module.exports.name = 'nestjs-graphql-transformer';
// you should change the version number anytime you change the configuration below - otherwise, jest will not detect changes
module.exports.version = 1;

module.exports.factory = (cs) => {
  return transformer.before(
    {
      // @nestjs/graphql/plugin options (can be empty)
    },
    cs.program, // "cs.tsCompiler.program" for older versions of Jest (<= v27)
  );
};

With this in place, import AST transformer within your jest configuration file. By default (in the starter application), e2e tests configuration file is located under the test folder and is named jest-e2e.json.

{
  ... // other configuration
  "globals": {
    "ts-jest": {
      "astTransformers": {
        "before": ["<path to the file created above>"]
      }
    }
  }
}

If you use jest@^29, then use the snippet below, as the previous approach got deprecated.

{
  ... // other configuration
  "transform": {
    "^.+\\.(t|j)s$": [
      "ts-jest",
      {
        "astTransformers": {
          "before": ["<path to the file created above>"]
        }
      }
    ]
  }
}