在 OpenAPI 中,paths 是指 API 暴露的端点(例如 /users 或 /reports/summary),而 operations 则是用于处理这些路径的 HTTP 方法,例如 GET、POST 或 DELETE。
如果要将控制器关联到特定标签,可以使用 @ApiTags(...tags) 装饰器。
@ApiTags('cats')
@Controller('cats')
export class CatsController {}如果需要定义请求中期望包含的自定义请求头,可以使用 @ApiHeader() 装饰器。
@ApiHeader({
name: 'X-MyHeader',
description: '自定义请求头',
})
@Controller('cats')
export class CatsController {}你可以使用 @ApiResponse() 装饰器来为端点添加响应的详细描述。
@Post()
@ApiResponse({ status: 201, description: '资源已成功创建。' })
@ApiResponse({ status: 403, description: '无权访问此资源。' })
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto)
}为了简化常见 HTTP 响应的声明,Nest 提供了一系列派生自 @ApiResponse() 的简写装饰器:
@ApiOkResponse()@ApiCreatedResponse()@ApiAcceptedResponse()@ApiNoContentResponse()@ApiMovedPermanentlyResponse()@ApiFoundResponse()@ApiBadRequestResponse()@ApiUnauthorizedResponse()@ApiNotFoundResponse()@ApiForbiddenResponse()@ApiMethodNotAllowedResponse()@ApiNotAcceptableResponse()@ApiRequestTimeoutResponse()@ApiConflictResponse()@ApiPreconditionFailedResponse()@ApiTooManyRequestsResponse()@ApiGoneResponse()@ApiPayloadTooLargeResponse()@ApiUnsupportedMediaTypeResponse()@ApiUnprocessableEntityResponse()@ApiInternalServerErrorResponse()@ApiNotImplementedResponse()@ApiBadGatewayResponse()@ApiServiceUnavailableResponse()@ApiGatewayTimeoutResponse()@ApiDefaultResponse()使用简写装饰器,上面的示例可以改写为:
@Post()
@ApiCreatedResponse({ description: '资源已成功创建。' })
@ApiForbiddenResponse({ description: '无权访问此资源。' })
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto)
}要让 Swagger UI 展示 API 响应的数据结构,你需要先定义一个模型或 DTO 类,并使用 @ApiProperty() 装饰器注解其所有属性。
export class Cat {
@ApiProperty({ description: '猫的 ID', example: 1 })
id: number
@ApiProperty({ description: '猫的名字', example: 'Mimi' })
name: string
@ApiProperty({ description: '猫的年龄', example: 2 })
age: number
@ApiProperty({ description: '猫的品种', example: '布偶' })
breed: string
}然后,在响应装饰器(如 @ApiCreatedResponse)中,通过 type 属性来指定这个模型。
@ApiTags('cats')
@Controller('cats')
export class CatsController {
@Post()
@ApiCreatedResponse({
description: 'Cat 记录已成功创建。',
type: Cat,
})
async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
return this.catsService.create(createCatDto)
}
}配置完成后,访问 Swagger UI,就可以看到 Cat 模型已经被正确地解析和展示为响应体结构:

如果你不希望为每个端点或控制器重复定义相同的响应,可以通过 DocumentBuilder 来设置全局响应,这对于统一定义通用响应(例如 401 Unauthorized 或 500 Internal Server Error 等错误)非常有用。
const config = new DocumentBuilder()
.addGlobalResponse({
status: 500,
description: '内部服务器错误',
})
// 其他配置
.build()要启用文件上传功能,你可以组合使用 @ApiBody() 和 @ApiConsumes() 装饰器。下面是一个结合文件上传功能的完整示例:
@UseInterceptors(FileInterceptor('file'))
@ApiConsumes('multipart/form-data')
@ApiBody({
description: '新的猫咪信息',
type: FileUploadDto,
})
uploadFile(@UploadedFile() file: Express.Multer.File) {}对应的 FileUploadDto 定义如下:
class FileUploadDto {
@ApiProperty({ type: 'string', format: 'binary' })
file: any
}如需上传多个文件,则可按如下方式定义 FilesUploadDto:
class FilesUploadDto {
@ApiProperty({ type: 'array', items: { type: 'string', format: 'binary' } })
files: any[]
}你可以使用 @ApiExtension() 装饰器为请求添加「扩展」(Extension)。注意,扩展名必须以 x- 作为前缀。
@ApiExtension('x-foo', { hello: 'world' })ApiResponse我们可以通过提供原始定义的方式,为 Swagger UI 定义通用(或称"泛型")的 Schema。假设有如下一个 DTO:
export class PaginatedDto<TData> {
@ApiProperty()
total: number
@ApiProperty()
limit: number
@ApiProperty()
offset: number
results: TData[]
}我们没有为 results 属性添加装饰器,因为稍后将通过原始定义(Raw Definition)来指定其类型。接下来,再定义另一个 DTO,例如 CatDto:
export class CatDto {
@ApiProperty()
name: string
@ApiProperty()
age: number
@ApiProperty()
breed: string
}基于以上定义,我们可以这样来定义 PaginatedDto<CatDto> 的响应类型:
@ApiOkResponse({
schema: {
allOf: [
{ $ref: getSchemaPath(PaginatedDto) },
{
properties: {
results: {
type: 'array',
items: { $ref: getSchemaPath(CatDto) },
},
},
},
],
},
})
async findAll(): Promise<PaginatedDto<CatDto>> {}在这个例子中,我们指定响应体将包含 PaginatedDto 的所有属性,同时 results 属性的类型为 Array<CatDto>。
getSchemaPath() 函数会根据指定的模型类,返回其在 OpenAPI 规范文件中的 Schema 路径。allOf 是 OpenAPI 规范 v3 (OAS 3) 中的一个概念,用于组合和继承 Schema,能够处理多种复杂的场景。最后,由于没有控制器直接引用 PaginatedDto,SwaggerModule 无法自动为它生成对应的模型定义。因此,我们需要使用 @ApiExtraModels() 装饰器将其显式地声明为额外模型。例如,在控制器层级添加该装饰器:
@Controller('cats')
@ApiExtraModels(PaginatedDto)
export class CatsController {}此时,该端点生成的 swagger.json 中的响应定义如下:
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/PaginatedDto"
},
{
"properties": {
"results": {
"$ref": "#/components/schemas/CatDto"
}
}
}
]
}
}
}
}
}为了便于复用,我们可以为 PaginatedDto 创建一个自定义装饰器,代码如下:
import { applyDecorators, Type } from '@nestjs/common'
import { ApiExtraModels, ApiOkResponse, getSchemaPath } from '@nestjs/swagger'
export const ApiPaginatedResponse = <TModel extends Type<any>>(
model: TModel
) => {
return applyDecorators(
ApiExtraModels(PaginatedDto, model),
ApiOkResponse({
schema: {
allOf: [
{ $ref: getSchemaPath(PaginatedDto) },
{
properties: {
results: {
type: 'array',
items: { $ref: getSchemaPath(model) },
},
},
},
],
},
})
)
}请注意,在上面的自定义装饰器中,我们不仅要注册 PaginatedDto,也要注册传入的 model(例如 CatDto),这样 SwaggerModule 才能为它们都生成对应的模型定义。
这样,我们就可以在端点上使用自定义的 @ApiPaginatedResponse() 装饰器了:
@ApiPaginatedResponse(CatDto)
async findAll(): Promise<PaginatedDto<CatDto>> {}然而,对于客户端代码生成工具而言,这种方式生成的 PaginatedResponse<TModel> 类型可能存在歧义。下面是一个基于上述 GET / 端点生成的客户端代码示例:
// Angular
findAll(): Observable<{ total: number, limit: number, offset: number, results: CatDto[] }>可以看到,这里生成的返回类型是一个匿名对象,缺乏明确的类型名称。为了解决这个问题,我们可以在 ApiPaginatedResponse 的 schema 中添加一个 title 属性:
export const ApiPaginatedResponse = <TModel extends Type<any>>(
model: TModel
) => {
return applyDecorators(
ApiOkResponse({
schema: {
title: `PaginatedResponseOf${model.name}`,
allOf: [
// ...
],
},
})
)
}添加 title 后,客户端代码生成工具将生成更清晰的结果:
// Angular
findAll(): Observable<PaginatedResponseOfCatDto>