TypeScript 的元数据反射系统有几个限制,例如,无法确定类由哪些属性组成,也无法识别给定属性是可选的还是必需的。但是,其中一些限制可以在编译时解决。Nest 提供了一个插件,可以增强 TypeScript 编译过程,以减少所需的样板代码量。
此插件是可选的。如果您愿意,您可以手动声明所有装饰器,或者仅在需要时声明特定的装饰器。
概述
Swagger 插件将自动:
- 使用
@ApiProperty
注释所有 DTO 属性,除非使用了@ApiHideProperty
- 根据问号设置
required
属性(例如name?: string
将设置required: false
) - 根据类型设置
type
或enum
属性(也支持数组) - 根据分配的默认值设置
default
属性 - 根据
class-validator
装饰器设置多个验证规则(如果classValidatorShim
设置为true
) - 为每个具有适当状态和
type
(响应模型)的端点添加响应装饰器 - 根据注释生成属性和端点的描述(如果
introspectComments
设置为true
) - 根据注释生成属性的示例值(如果
introspectComments
设置为true
)
请注意,您的文件名必须具有以下之一以下后缀:['.dto.ts', '.entity.ts']
(例如,create-user.dto.ts
),以便插件进行分析。
如果您使用不同的后缀,您可以通过指定dtoFileNameSuffix
选项来调整插件的行为(见下文)。
以前,如果您想通过 Swagger UI 提供交互式体验,
您必须复制大量代码以让包知道您的模型/组件应如何在规范中声明。例如,您可以定义一个简单的CreateUserDto
类,如下所示:
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 插件,可以简单地声明上述类定义:
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
属性:
/**
* A list of user's roles
* @example ['admin']
*/
@ApiProperty({
description: `A list of user's roles`,
example: ['admin'],
})
roles: RoleEnum[] = [];
您必须复制描述和示例值。启用introspectComments
后,CLI 插件可以提取这些注释并自动为属性提供描述(以及示例,如果已定义)。现在,上述属性可以简单地声明如下:
/**
* A list of user's roles
* @example ['admin']
*/
roles: RoleEnum[] = [];
您可以使用dtoKeyOfComment
和controllerKeyOfComment
插件选项来自定义插件如何分别设置ApiProperty
和ApiOperation
装饰器的值。请看以下示例:
export class SomeController {
/**
* Create some resource
*/
@Post()
create() {}
}
默认情况下,这些选项设置为description
。这意味着插件将为ApiOperation
运算符上的description
键分配创建一些资源
。如下所示:
@ApiOperation({ description: "Create some resource" })
对于模型,适用相同的逻辑,但改为使用ApiProperty
装饰器。
Using the CLI plugin
要启用插件,请打开nest-cli.json
(如果您使用Nest CLI)并添加以下plugins
配置:
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": ["@nestjs/swagger"]
}
}
您可以使用 options
属性来自定义插件的行为。
{
"plugins": [
{
"name": "@nestjs/swagger",
"options": {
"classValidatorShim": false,
"introspectComments": true
}
}
]
}
options
属性必须满足以下接口:
export interface PluginOptions {
dtoFileNameSuffix?: string[]
controllerFileNameSuffix?: string[]
classValidatorShim?: boolean
dtoKeyOfComment?: string
controllerKeyOfComment?: string
introspectComments?: boolean
}
Option | Default | Description |
---|---|---|
dtoFileNameSuffix | ['.dto.ts', '.entity.ts'] | DTO (Data Transfer Object) files suffix |
controllerFileNameSuffix | .controller.ts | Controller files suffix |
classValidatorShim | true | If 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 . |
introspectComments | false | If set to true, plugin will generate descriptions and example values for properties based on comments |
确保在插件选项更新时删除 /dist
文件夹并重建应用程序。
如果您不使用 CLI,而是使用自定义 webpack
配置,则可以将此插件与 ts-loader
结合使用:
getCustomTransformers: (program: any) => ({
before: [require('@nestjs/swagger/plugin').before({}, program)]
}),
SWC 构建器
对于标准设置(非 monorepo),要将 CLI 插件与 SWC 构建器一起使用,您需要启用类型检查,如 此处 所述。
$ nest start -b swc --type-check
对于 monorepo 设置,请按照 此处 的说明进行操作。
$ npx ts-node src/generate-metadata.ts
# 或 npx ts-node apps/{YOUR_APP}/src/generate-metadata.ts
现在,必须通过 SwaggerModule#loadPluginMetadata
方法加载序列化的元数据文件,如下所示:
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 测试目录中创建以下文件:
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
。
{
"globals": {
"ts-jest": {
"astTransformers": {
"before": ["<path to the file created above>"]
}
}
}
}
如果您使用jest@^29
,那么请使用下面的代码片段,因为以前的方法已被弃用。
{
"transform": {
"^.+\\.(t|j)s$": [
"ts-jest",
{
"astTransformers": {
"before": ["<path to the file created above>"]
}
}
]
}
}
排除 jest
故障(e2e 测试)
如果 jest
似乎没有获取您的配置更改,则 Jest 可能已经 缓存 了构建结果。要应用新配置,您需要清除 Jest 的缓存目录。
要清除缓存目录,请在 NestJS 项目文件夹中运行以下命令:
$ npx jest --clearCache
如果自动缓存清除失败,您仍然可以使用以下命令手动删除缓存文件夹:
# 通过在 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