命令行插件 CLI Plugin

导读

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

提示

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

概述

Swagger 插件将自动:

  • 使用 @ApiProperty 注释所有 DTO 属性,除非使用了 @ApiHideProperty
  • 根据问号设置 required 属性(例如 name?: string 将设置 required: false
  • 根据类型设置 typeenum 属性(也支持数组)
  • 根据分配的默认值设置 default 属性
  • 根据 class-validator 装饰器设置多个验证规则(如果 classValidatorShim 设置为 true
  • 为每个具有适当状态和 type(响应模型)的端点添加响应装饰器
  • 根据注释生成属性和端点的描述(如果 introspectComments 设置为 true
  • 根据注释生成属性的示例值(如果 introspectComments 设置为 true

请注意,您的文件名必须具有以下之一以下后缀:['.dto.ts', '.entity.ts'](例如,create-user.dto.ts),以便插件进行分析。

如果您使用不同的后缀,您可以通过指定dtoFileNameSuffix选项来调整插件的行为(见下文)。

以前,如果您想通过 Swagger UI 提供交互式体验, 您必须复制大量代码以让包知道您的模型/组件应如何在规范中声明。例如,您可以定义一个简单的CreateUserDto类,如下所示:

ts
export class CreateUserDto {
  @ApiProperty()
  email: string

  @ApiProperty()
  password: string

  @ApiProperty({ enum: RoleEnum, default: [], isArray: true })
  roles: RoleEnum[] = []

  @ApiProperty({ required: false, default: true })
  isEnabled?: boolean = true
}

虽然对于中型项目来说这不是一个重大问题,但一旦拥有大量类,它就会变得冗长且难以维护。

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

ts
export class CreateUserDto {
  email: string
  password: string
  roles: RoleEnum[] = []
  isEnabled?: boolean = true
}
注意

Swagger 插件将从 TypeScript 类型和类验证器装饰器中派生出 @ApiProperty() 注释。这有助于清楚地描述生成的 Swagger UI 文档的 API。但是,运行时验证仍将由类验证器装饰器处理。因此,需要继续使用诸如 IsEmail()IsNumber() 等验证器。

因此,如果您打算依靠自动注释来生成文档,并且仍然希望进行运行时验证,那么类验证器装饰器仍然是必要的。

提示

在 DTO 中使用 映射类型实用程序(如 PartialType)时,请从 @nestjs/swagger 而不是 @nestjs/mapped-types 导入它们,以便插件获取架构。

该插件会根据抽象语法树动态添加适当的装饰器。因此,您不必费力处理散布在代码中的 @ApiProperty 装饰器。

提示

该插件将自动生成任何缺失的 swagger 属性,但如果您需要覆盖它们,只需通过 @ApiProperty() 明确设置它们即可。

注释自省

启用注释自省功能后,CLI 插件将根据注释为属性生成描述和示例值。

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

ts
/**
 * A list of user's roles
 * @example ['admin']
 */
@ApiProperty({
  description: `A list of user's roles`,
  example: ['admin'],
})
roles: RoleEnum[] = [];

您必须复制描述和示例值。启用introspectComments后,CLI 插件可以提取这些注释并自动为属性提供描述(以及示例,如果已定义)。现在,上述属性可以简单地声明如下:

ts
/**
 * A list of user's roles
 * @example ['admin']
 */
roles: RoleEnum[] = [];

您可以使用dtoKeyOfCommentcontrollerKeyOfComment插件选项来自定义插件如何分别设置ApiPropertyApiOperation装饰器的值。请看以下示例:

ts
export class SomeController {
  /**
   * Create some resource
   */
  @Post()
  create() {}
}

默认情况下,这些选项设置为description。这意味着插件将为ApiOperation运算符上的description键分配创建一些资源。如下所示:

ts
@ApiOperation({ description: "Create some resource" })
Hint

对于模型,适用相同的逻辑,但改为使用ApiProperty装饰器。

Using the CLI plugin

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

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

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

json
{
  "plugins": [
    {
      "name": "@nestjs/swagger",
      "options": {
        "classValidatorShim": false,
        "introspectComments": true
      }
    }
  ]
}

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

ts
export interface PluginOptions {
  dtoFileNameSuffix?: string[]
  controllerFileNameSuffix?: string[]
  classValidatorShim?: boolean
  dtoKeyOfComment?: string
  controllerKeyOfComment?: string
  introspectComments?: boolean
}
OptionDefaultDescription
dtoFileNameSuffix['.dto.ts', '.entity.ts']DTO (Data Transfer Object) files suffix
controllerFileNameSuffix.controller.tsController files suffix
classValidatorShimtrueIf set to true, the module will reuse class-validator validation decorators (e.g. @Max(10) will add max: 10 to schema definition)
dtoKeyOfComment'description'The property key to set the comment text to on ApiProperty.
controllerKeyOfComment'description'The property key to set the comment text to on ApiOperation.
introspectCommentsfalseIf set to true, plugin will generate descriptions and example values for properties based on comments

确保在插件选项更新时删除 /dist 文件夹并重建应用程序。 如果您不使用 CLI,而是使用自定义 webpack 配置,则可以将此插件与 ts-loader 结合使用:

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

SWC 构建器

对于标准设置(非 monorepo),要将 CLI 插件与 SWC 构建器一起使用,您需要启用类型检查,如 此处 所述。

bash
$ nest start -b swc --type-check

对于 monorepo 设置,请按照 此处 的说明进行操作。

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

现在,必须通过 SwaggerModule#loadPluginMetadata 方法加载序列化的元数据文件,如下所示:

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

await SwaggerModule.loadPluginMetadata(metadata) // <-- here
const document = SwaggerModule.createDocument(app, config)

ts-jest 集成(e2e 测试)

要运行 e2e 测试,ts-jest 会在内存中动态编译您的源代码文件。这意味着,它不使用 Nest CLI 编译器,也不应用任何插件或执行 AST 转换。

要启用该插件,请在 e2e 测试目录中创建以下文件:

js
const transformer = require('@nestjs/swagger/plugin')

module.exports.name = 'nestjs-swagger-transformer'
// 您应该在更改以下配置时随时更改版本号 - 否则,jest 将无法检测到更改
module.exports.version = 1

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

完成这些后,在jest配置文件中导入 AST 转换器。默认情况下(在启动应用程序中),e2e 测试配置文件位于test文件夹下,名为jest-e2e.json

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

如果您使用jest@^29,那么请使用下面的代码片段,因为以前的方法已被弃用。

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

排除 jest 故障(e2e 测试)

如果 jest 似乎没有获取您的配置更改,则 Jest 可能已经 缓存 了构建结果。要应用新配置,您需要清除 Jest 的缓存目录。

要清除缓存目录,请在 NestJS 项目文件夹中运行以下命令:

bash
$ npx jest --clearCache

如果自动缓存清除失败,您仍然可以使用以下命令手动删除缓存文件夹:

bash
# 通过在 NestJS 项目根目录中运行以下命令找到 jest 缓存目录(通常为 /tmp/jest_rs)
#
$ npx jest --showConfig | grep cache
# ex result:
#   "cache": true,
#   "cacheDirectory": "/tmp/jest_rs"

# Remove or empty the Jest cache directory
$ rm -rf  <cacheDirectory value>
# ex:
# rm -rf /tmp/jest_rs