Swagger 与 OpenAPI
本教程共 47 篇 · 第 41 篇 · 更新于 2026-08-09 · 约 12 分钟阅读
本节目标:学会在 NestJS 中集成 Swagger,用装饰器自动生成完整的 API 文档,让前端同事不再追着你要接口文档。
为什么需要 API 文档
你写的 API 有 50 个接口,每个接口的参数、返回值都不一样。没有文档的话,前端同事只能靠猜或者靠问你。
OpenAPI(以前叫 Swagger)是一套描述 RESTful API 的标准格式。你用一套标准格式把接口描述清楚,然后就能自动生成漂亮的交互式文档页面。前端同事打开网页就能看到所有接口,还能直接在线调试。
NestJS 跟 Swagger 的集成非常丝滑。因为 NestJS 本身就有丰富的装饰器和类型信息,大部分文档可以自动生成,你只需要补一些细节就行。
安装
npm i --save @nestjs/swagger
就这一个包,搞定一切。
快速上手
在 main.ts 里加几行代码就能跑起来:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('猫咪 API')
.setDescription('一个管理猫咪的接口服务')
.setVersion('1.0')
.addTag('cats')
.build();
const documentFactory = () => SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, documentFactory);
await app.listen(3000);
}
bootstrap();
启动服务后,打开 http://localhost:3000/api 就能看到 Swagger UI 页面了。
这段代码做了三件事:
- 用
DocumentBuilder构建文档的基本信息(标题、描述、版本) - 用
SwaggerModule.createDocument()扫描所有路由,生成完整的 OpenAPI 文档 - 用
SwaggerModule.setup()把文档挂载到/api路径
Tip
createDocument()用了工厂函数的方式,只有在你真正请求文档时才会生成。这样能节省启动时间。访问/api-json可以拿到 JSON 格式的文档,/api-yaml拿 YAML 格式。
标注 DTO 模型
光启动 Swagger 还不够,它不知道你的请求体和返回值长什么样。你需要用 @ApiProperty() 装饰器来标注 DTO 的每个字段。
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty({ description: '猫咪的名字', example: '咪咪' })
name: string;
@ApiProperty({ description: '猫咪的年龄', example: 3, minimum: 0 })
age: number;
@ApiPropertyOptional({ description: '猫咪的品种', example: '橘猫' })
breed?: string;
}
@ApiProperty() 常用的配置项:
| 配置项 | 说明 |
|---|---|
description | 字段描述 |
example | 示例值 |
required | 是否必填(默认 true) |
minimum / maximum | 数值范围 |
minLength / maxLength | 字符串长度范围 |
enum | 枚举值列表 |
type | 字段类型(复杂类型时需要) |
isArray | 是否是数组 |
如果字段是可选的,用 @ApiPropertyOptional(),效果跟 @ApiProperty({ required: false }) 一样。
Note如果你用了 class-validator 的装饰器(比如
@IsString()、@Min()),Swagger 插件可以自动从这些装饰器里提取信息,省去手写@ApiProperty()参数的工作。后面会讲这个插件。
标注控制器
DTO 标好了,接下来标注控制器里的接口:
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';
import { CatDto } from './dto/cat.dto';
import { CreateCatDto } from './dto/create-cat.dto';
@ApiTags('cats') // 分组标签
@Controller('cats')
export class CatsController {
@Post()
@ApiOperation({ summary: '创建一只猫' })
@ApiResponse({ status: 201, description: '创建成功', type: CatDto })
@ApiResponse({ status: 400, description: '参数错误' })
create(@Body() createCatDto: CreateCatDto): Promise<CatDto> {
return this.catsService.create(createCatDto);
}
@Get(':id')
@ApiOperation({ summary: '根据 ID 获取猫咪' })
@ApiParam({ name: 'id', description: '猫咪 ID' })
@ApiResponse({ status: 200, description: '查询成功', type: CatDto })
findOne(@Param('id') id: string): Promise<CatDto> {
return this.catsService.findOne(id);
}
}
常用的控制器装饰器:
| 装饰器 | 作用 | 用在哪 |
|---|---|---|
@ApiTags() | 接口分组标签 | 控制器 / 方法 |
@ApiOperation() | 接口描述(summary) | 方法 |
@ApiResponse() | 描述响应状态和类型 | 方法 / 控制器 |
@ApiParam() | 描述路径参数 | 方法 / 控制器 |
@ApiQuery() | 描述查询参数 | 方法 / 控制器 |
@ApiBody() | 描述请求体 | 方法 |
@ApiHeader() | 描述请求头 | 方法 / 控制器 |
@ApiExcludeEndpoint() | 排除某个接口 | 方法 |
@ApiExcludeController() | 排除整个控制器 | 控制器 |
认证配置
如果你的接口需要认证,Swagger 文档里也要体现出来。
Bearer Token
const config = new DocumentBuilder()
.setTitle('猫咪 API')
.addBearerAuth() // 添加 Bearer Token 认证
.build();
然后在需要认证的控制器或方法上加装饰器:
@ApiBearerAuth()
@Controller('cats')
export class CatsController {
// 这个控制器下的所有接口都会显示需要 Bearer Token
}
其他认证方式
DocumentBuilder 还支持其他认证方式:
const config = new DocumentBuilder()
.addBasicAuth() // HTTP Basic 认证
.addCookieAuth() // Cookie 认证
.addOAuth2() // OAuth2 认证
.addApiKey() // API Key 认证
.build();
对应的装饰器:
@ApiBasicAuth() // 方法/控制器级别
@ApiCookieAuth()
@ApiOAuth2()
@ApiSecurity('api_key')
自动生成的 Swagger 插件
每次都要手动写 @ApiProperty() 的参数,挺烦的。NestJS 提供了一个编译器插件,能自动从代码里提取信息。
配置
在 nest-cli.json 里启用:
{
"compilerOptions": {
"plugins": ["@nestjs/swagger"]
}
}
启用后,插件会自动做这些事:
- 从
@ApiProperty()标注的字段类型推断type,不用手动写 - 从 class-validator 装饰器(
@IsString()、@Min()等)提取校验规则 - 根据字段名和类型自动生成
description - 可选字段(带
?)自动标记为required: false
这意味着你的 DTO 可以写得更简洁:
// 不用插件时
@ApiProperty({ type: String, description: '猫咪的名字' })
name: string;
// 用了插件后
@ApiProperty({ description: '猫咪的名字' })
name: string; // type 自动推断为 String
Tip插件还支持自定义配置,比如设置
dtoFileNameSuffix、controllerFileNameSuffix等。详细配置可以参考@nestjs/swagger的文档。
文档选项
createDocument() 的第三个参数可以精细控制文档生成行为:
const documentFactory = () =>
SwaggerModule.createDocument(app, config, {
include: [CatsModule], // 只包含指定模块
ignoreGlobalPrefix: true, // 忽略全局前缀
deepScanRoutes: true, // 深度扫描路由
autoTagControllers: true, // 自动根据控制器名生成标签
operationIdFactory: (controllerKey, methodKey) => methodKey, // 自定义 operationId
});
几个实用的选项:
| 选项 | 说明 |
|---|---|
include | 只生成指定模块的文档 |
ignoreGlobalPrefix | 忽略 setGlobalPrefix() 设置的前缀 |
deepScanRoutes | 深度扫描 include 模块导入的子模块路由 |
extraModels | 额外包含的模型(不在路由里但想出现在文档里的) |
autoTagControllers | 自动用控制器名(去掉 Controller 后缀)作为标签 |
UI 定制
SwaggerModule.setup() 的第四个参数可以定制 Swagger UI 的外观和行为:
SwaggerModule.setup('api', app, documentFactory, {
customSiteTitle: '猫咪 API 文档', // 网页标题
customFavIcon: '/favicon.ico', // 网站图标
customCss: '.swagger-ui .topbar { display: none }', // 自定义 CSS
swaggerOptions: {
persistAuthorization: true, // 刷新页面保留认证信息
docExpansion: 'list', // 默认展开级别:none/list/full
},
});
常用的 UI 配置:
| 选项 | 说明 |
|---|---|
customSiteTitle | 网页标题 |
customFavIcon | 网站图标 URL |
customCss | 自定义 CSS 样式 |
customJs | 自定义 JS 文件 |
swaggerOptions | Swagger UI 原生配置 |
useGlobalPrefix | 是否使用全局前缀 |
ui | 设为 false 关闭 UI(只保留 JSON/YAML) |
raw | 控制 JSON/YAML 文档的暴露 |
Tip
persistAuthorization: true这个选项很实用。默认情况下,你刷新页面后填好的 Token 就没了。开了这个选项后,Token 会保存在浏览器里。
隐藏接口和字段
有些接口不想暴露在文档里(比如内部调试用的),用 @ApiExcludeEndpoint() 就行:
@Get('debug')
@ApiExcludeEndpoint() // 这个接口不会出现在 Swagger 文档里
debug() {
return 'debug info';
}
DTO 里有些字段不想暴露,用 @ApiHideProperty():
export class UserDto {
@ApiProperty()
name: string;
@ApiHideProperty()
password: string; // 不会出现在文档里
}
多个文档版本
如果你的 API 有多个版本(v1、v2),可以生成多份文档:
const configV1 = new DocumentBuilder()
.setTitle('API v1')
.setVersion('1.0')
.build();
const configV2 = new DocumentBuilder()
.setTitle('API v2')
.setVersion('2.0')
.build();
const documentV1 = () => SwaggerModule.createDocument(app, configV1, {
include: [V1Module],
});
const documentV2 = () => SwaggerModule.createDocument(app, configV2, {
include: [V2Module],
});
SwaggerModule.setup('api/v1', app, documentV1);
SwaggerModule.setup('api/v2', app, documentV2);
导出文档
除了在线查看,你还可以把文档导出为 JSON 或 YAML 文件:
import * as fs from 'fs';
const document = SwaggerModule.createDocument(app, config);
fs.writeFileSync('./swagger-doc.json', JSON.stringify(document, null, 2));
访问 /api-json 也能直接下载 JSON 格式的文档。
Tip踩坑经验:如果用了
helmet中间件,Swagger UI 可能会因为 CSP(内容安全策略)加载不出来。解决方法是调整 CSP 配置:app.register(helmet, { contentSecurityPolicy: { directives: { styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'validator.swagger.io'], scriptSrc: ["'self'", 'https:', "'unsafe-inline'"], }, }, });
装饰器速查表
| 装饰器 | 作用 | 用在哪 |
|---|---|---|
@ApiTags() | 分组标签 | 控制器/方法 |
@ApiOperation() | 接口描述 | 方法 |
@ApiResponse() | 响应描述 | 方法/控制器 |
@ApiParam() | 路径参数 | 方法/控制器 |
@ApiQuery() | 查询参数 | 方法/控制器 |
@ApiBody() | 请求体 | 方法 |
@ApiHeader() | 请求头 | 方法/控制器 |
@ApiProperty() | 模型属性 | 模型 |
@ApiPropertyOptional() | 可选模型属性 | 模型 |
@ApiHideProperty() | 隐藏模型属性 | 模型 |
@ApiBearerAuth() | Bearer 认证 | 方法/控制器 |
@ApiBasicAuth() | Basic 认证 | 方法/控制器 |
@ApiCookieAuth() | Cookie 认证 | 方法/控制器 |
@ApiOAuth2() | OAuth2 认证 | 方法/控制器 |
@ApiExcludeEndpoint() | 排除接口 | 方法 |
@ApiExcludeController() | 排除控制器 | 控制器 |
@ApiExtraModels() | 额外模型 | 方法/控制器 |
@ApiConsumes() | 请求 Content-Type | 方法/控制器 |
@ApiProduces() | 响应 Content-Type | 方法/控制器 |
@ApiSecurity() | 安全方案 | 方法/控制器 |
@ApiExtension() | 自定义扩展 | 方法 |
@ApiCallbacks() | 回调定义 | 方法/控制器 |
@ApiSchema() | Schema 配置 | 模型 |
小结
本章覆盖了 NestJS 集成 Swagger/OpenAPI 的核心内容:
- 安装
@nestjs/swagger,在main.ts里几行代码就能启动 - 用
@ApiProperty()标注 DTO 模型,让文档知道字段长什么样 - 用
@ApiOperation()、@ApiResponse()等标注控制器 - 用
DocumentBuilder配置认证(Bearer、Basic、OAuth2 等) - Swagger 编译器插件能自动提取类型和校验信息
- 可以定制 UI 外观、隐藏接口、导出文档
- 支持多版本文档
有了 Swagger,你的 API 就有了自动更新的交互式文档。前端同事可以自己看文档、调试接口,再也不用追着你问了。