【NestJS】 OpenAPI文档:运行时动态生成揭秘
NestJS 的 OpenAPI 文档是“运行时动态生成”的,而不是编译时静态生成的。
🚀 一、结论先行
| 特性 | NestJS Swagger(@nestjs/swagger) |
|---|---|
| 文档生成方式 | ✅ 运行时动态生成 |
| 是否依赖反射元数据 | ✅ 依赖 reflect-metadata |
| 是否自动更新 | ✅ 每次启动应用都会重新生成 |
| 是否输出静态文件 | ❌ 默认不会(除非手动导出) |
| 是否可缓存 / 可导出 | ✅ 可用 SwaggerModule.createDocument() 导出静态 JSON |
🧩 二、Nest 是怎么生成 OpenAPI 的?
Nest 使用了一个包:
@nestjs/swagger
它在程序启动时扫描整个项目(控制器、DTO、装饰器),
通过 Reflect API 提取出由装饰器写入的元数据,拼成一份 OpenAPI 规范文档。
🔍 执行流程:
1. 你在控制器中使用装饰器 → 写入元数据
2. Nest 启动时加载模块 → 收集这些元数据
3. SwaggerModule.createDocument() → 读取 Reflect 元数据,组装 JSON
4. Swagger UI 服务启动时 → 根据 JSON 渲染文档
也就是说:
文档并不是提前生成好放在磁盘上的,而是启动时在内存中动态生成的。
🧠 三、举个最小示例
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('My API').setDescription('NestJS OpenAPI Example').setVersion('1.0').build();// ⚙️ 核心:在运行时动态生成文档 JSONconst document = SwaggerModule.createDocument(app, config);// 提供交互式 Swagger UISwaggerModule.setup('docs', app, document);await app.listen(3000);
}
bootstrap();
当 createDocument() 执行时:
- 它会扫描所有 Controller;
- 读取方法、参数、DTO 上的元数据;
- 组装成一份内存中的 OpenAPI JSON 对象。
🪞 四、元数据来源:Reflect.defineMetadata
NestJS 的 Swagger 插件在编译阶段不会生成文件,而是在装饰器执行时写元数据。
比如:
@ApiTags('User')
@Controller('users')
export class UserController {@ApiOperation({ summary: '获取用户列表' })@ApiResponse({ status: 200, type: UserDto })@Get()getUsers() {}
}
这些装饰器做的事情其实就是:
Reflect.defineMetadata('swagger/apiOperation', { summary: '获取用户列表' }, UserController.prototype.getUsers);
然后 SwaggerScanner 在 createDocument() 里:
- 遍历所有控制器;
- 读取
Reflect.getMetadata(); - 拼出一份 JSON。
⚙️ 五、这份 JSON 长什么样?
如果你打印:
console.log(JSON.stringify(document, null, 2));
会看到:
{"openapi": "3.0.0","info": {"title": "My API","version": "1.0"},"paths": {"/users": {"get": {"summary": "获取用户列表","responses": { "200": { "description": "OK" } }}}}
}
这个 JSON 就是 Swagger UI 展示用的文档。
🧠 它完全在内存中生成,不会自动落地到磁盘。
📦 六、静态导出(手动)
如果你希望把文档导出成静态文件,也可以手动写:
import * as fs from 'fs';const document = SwaggerModule.createDocument(app, config);
fs.writeFileSync('./openapi.json', JSON.stringify(document, null, 2));
这时它就变成“静态 OpenAPI 文件”,可以部署到外部文档服务器,比如:
- Redoc
- Stoplight
- SwaggerHub
🔄 七、运行时 vs 静态生成对比
| 特性 | 运行时动态生成(Nest 默认) | 静态生成(手动导出) |
|---|---|---|
| 生成时机 | 应用启动时 | 手动运行导出命令 |
| 文件落地 | ❌ 不会生成文件 | ✅ 可导出 JSON |
| 是否依赖反射 | ✅ 是 | ✅ 是 |
| 可否自动更新 | ✅ 每次启动都会更新 | ❌ 必须重新导出 |
| 性能 | 启动时略慢 | 启动快,构建时多一步 |
| 常见场景 | 本地开发、动态文档 UI | CI/CD、第三方平台集成 |
🧭 八、再深入一点:运行时动态生成的本质
Nest 启动时,会经过以下几步:
AppModule 加载├──> MetadataScanner 扫描所有装饰器├──> DiscoveryService 收集所有 provider/controller├──> SwaggerScanner 提取元数据(路径、参数、响应等)├──> DocumentBuilder 构建 OpenAPI 对象└──> SwaggerUI 渲染 JSON
所有这些都发生在 bootstrap() 阶段的内存中,没有写文件。
因此文档数据是 动态构造的运行时快照。
🎯 九、总结一句话记忆
NestJS 的 OpenAPI 文档是基于 Reflect 元数据,在运行时动态扫描生成的;
但你可以通过SwaggerModule.createDocument()手动导出静态 JSON 文件。
🔧 十、实战建议
| 场景 | 推荐方式 |
|---|---|
| 本地开发 | 动态生成 + Swagger UI(默认方式) |
| CI/CD 集成 | 在构建阶段执行导出脚本,生成 openapi.json |
| 前端 SDK 自动生成 | 使用导出的 JSON + openapi-generator-cli |
| 性能敏感场景 | 延迟加载 Swagger,只在开发模式启用 |
