Skip to content

集成 Swagger

@nestjs/swagger 根据 Controller、路由参数和 DTO 生成 OpenAPI 文档,并提供可交互的 Swagger UI。

1. 安装依赖

安装 NestJS 官方 Swagger 模块:

bash
pnpm add @nestjs/swagger

2. 配置启用条件

Swagger 文档可能暴露接口结构和调试能力,是否启用应由环境变量控制:

bash
SWAGGER_ENABLED=true

生产环境仅在确有需要时开启,并通过网关、VPN 或认证限制访问。

3. 在应用入口初始化

main.ts 中完成以下操作:

  1. 使用 DocumentBuilder 定义文档基本信息和认证方案。
  2. 使用 SwaggerModule.createDocument() 生成 OpenAPI 文档。
  3. 使用 SwaggerModule.setup() 挂载 Swagger UI 和 JSON 文档。
ts
import { NestFactory } from '@nestjs/core'
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'

import { AppModule } from './app.module'

async function bootstrap() {
  const app = await NestFactory.create(AppModule)

  if (process.env.SWAGGER_ENABLED === 'true') {
    const config = new DocumentBuilder()
      .setTitle('Hackathon API')
      .setDescription('Hackathon 服务接口')
      .setVersion('1.0')
      .addBearerAuth(undefined, 'bearer')
      .build()
    const documentFactory = () => SwaggerModule.createDocument(app, config)

    SwaggerModule.setup('docs', app, documentFactory, {
      jsonDocumentUrl: 'docs/openapi.json',
    })
  }

  await app.listen(process.env.PORT ?? 3000)
}

void bootstrap()

addBearerAuth(undefined, 'bearer') 注册名为 bearer 的 Bearer Token 认证方案。后续使用 @ApiBearerAuth('bearer') 时,名称必须保持一致。

4. 描述 DTO Schema

TypeScript 的运行时反射无法获得完整的字段信息。使用 @ApiProperty()@ApiPropertyOptional() 描述请求、响应 DTO 的字段、格式和示例:

ts
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'

export class CreateUserDto {
  @ApiProperty({ example: 'vfan@example.com' })
  email: string

  @ApiProperty({ minLength: 8 })
  password: string

  @ApiPropertyOptional({ example: 'Vfan' })
  nickname?: string
}

export class UserResponseDto {
  @ApiProperty({ format: 'uuid' })
  id: string

  @ApiProperty({ example: 'vfan@example.com' })
  email: string
}

响应 DTO 不应包含密码、Refresh Token 或内部状态等敏感字段。数组、泛型、联合类型等无法自动推断时,需要显式声明 Schema。

常用装饰器:

装饰器用途
@ApiProperty()描述必填字段的类型、格式、示例和约束
@ApiPropertyOptional()描述可选字段,等同于设置 required: false
@ApiHideProperty()从生成的 OpenAPI Schema 中隐藏指定字段
@ApiExtraModels()注册未被 Controller 直接引用的额外 DTO 模型

5. 标注 Controller

为 Controller 添加资源分组,并为路由声明操作说明、认证要求和主要响应:

ts
import { Controller, Get, NotFoundException, Param } from '@nestjs/common'
import { ApiBearerAuth, ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'

import { UserResponseDto } from './dto/user-response.dto'
import { UsersService } from './users.service'

@ApiTags('users')
@ApiBearerAuth('bearer')
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get(':id')
  @ApiOperation({ summary: '查询用户' })
  @ApiOkResponse({ type: UserResponseDto })
  @ApiNotFoundResponse({ description: '用户不存在' })
  async findOne(@Param('id') id: string): Promise<UserResponseDto> {
    const user = await this.usersService.findOne(id)
    if (!user) throw new NotFoundException('用户不存在')
    return user
  }
}

公开端点不添加 @ApiBearerAuth();当项目使用全局 Guard 时,Swagger 中的安全声明仍应与实际路由保持一致。

常用装饰器:

装饰器用途
@ApiTags()对 Controller 进行资源分组
@ApiOperation()描述接口用途
@ApiBearerAuth()声明接口需要 Bearer Token
@ApiOkResponse()描述成功响应
@ApiBadRequestResponse()描述参数错误响应
@ApiParam()补充路径参数约束
@ApiQuery()补充查询参数约束

6. 启动并检查文档

bash
pnpm start:dev

应用启动后访问:

  • Swagger UI:http://localhost:3000/docs
  • OpenAPI JSON:http://localhost:3000/docs/openapi.json

Swagger UI 应能显示 Controller 路由、请求参数、DTO Schema、响应类型和认证入口。若启用了全局路由前缀,Swagger 文档路径默认不自动添加该前缀;需要保持一致时,在 SwaggerModule.setup() 的选项中设置 useGlobalPrefix: true

维护原则

  • 路由、DTO 或鉴权方式变更时,同步检查生成的 OpenAPI JSON。
  • @ApiQuery()@ApiParam() 补充无法自动推断的参数约束。
  • 将生成的 OpenAPI JSON 纳入契约检查或客户端生成流程前,固定接口版本并评估破坏性变更。

参考

基于 MIT 许可发布