不只是一份
Swagger 文档。

nestjs-docfy 通过 companion 文件命名约定,把 Swagger/OpenAPI 文档从控制器逻辑中分离出来,方式和 Nest 已经在用的 *.controller.spec.ts.

Installation
npm install nestjs-docfy
# peer deps
npm install @nestjs/common @nestjs/swagger reflect-metadata
最新版本 v0.13.0许可证 MIT
约定优于配置的 companion 文件
users.controller.ts → users.controller.docs.ts。和 Nest 已经在用的 *.controller.spec.ts 是同一套模式。
带 CI 门禁的 CLI
check、coverage --min、lint 和 patch-spec。文档缺失时直接让构建失败。
自动类型推断
接口、class-validator 和 @HttpCode() 无需额外装饰器即可变成 OpenAPI schema。
AI 优先的 Docfy UI
每个端点都带 Copy for AI 按钮的参考界面,特别适合粘贴进 LLM。

docfy-ui:AI 优先的参考查看器

nestjs-docfy 组装出的 OpenAPI spec,同样也是 docfy-ui 渲染的内容,不需要额外配置,用的是同一份事实来源。

  • Copy for AI:一份确定性生成、可直接喂给 LLM 的端点摘要,而不是带 $ref 的原始 JSON 转储
  • ⌘K 即时搜索所有端点
  • 每个端点完整的请求/响应细节,直接从 spec 生成
docfy-ui overview
docfy-ui busca com ⌘K
docfy-ui detalhe de endpoint com Copy for AI

实际用起来是什么样

之前:一个被装饰器淹没的控制器。之后:只剩路由本身,文档就在旁边的 companion 文件里。

users.controller.ts
@WithDocs()
@Controller('users')
export class UsersController {
  constructor(private readonly users: UsersService) {}

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.users.findOne(id);
  }
}
users.controller.docs.ts
import { docs } from 'nestjs-docfy';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { UsersController } from './users.controller';

docs(UsersController, {
  classDecorators: [ApiTags('users')],
  methods: {
    findOne: [
      ApiOperation({ summary: 'Get user by id' }),
      ApiResponse({ status: 200, description: 'OK', type: UserDto }),
      ApiResponse({ status: 404, description: 'User not found' }),
    ],
  },
});
只在启动时运行,零请求时开销和内联装饰器生成完全相同的 OpenAPI 输出Companion 文件约定,就像 *.spec.ts 一样

没有猴子补丁,没有运行时代理

只是恰到好处的时机,加上 Reflect 元数据。

01

写 companion 文件

users.controller.docs.ts 调用 docs(UsersController, { ... }),普通的 Swagger 装饰器,只是搬到了另一个文件里。

02

启动时被发现

DocfyModule.forRoot() 通过命名约定找到它,并在 SwaggerModule.createDocument() 运行之前,把 Reflect 元数据写到控制器的方法上。

03

完全一致的 OpenAPI 输出

SwaggerModule 看到的元数据,和装饰器直接写在控制器上时完全一样。