Last Sunday I shared nestjs-docfy, a small library to move Swagger decorators out of NestJS controllers into companion *.controller.docs.ts files. The reception was better than I expected, and a lot of the feedback pointed in the same direction: the separation is nice, but writing those docs files by hand is still tedious.
So I spent some time on that, and there's quite a bit new in this release.
A CLI that writes the boilerplate for you
The biggest addition is a generate command that reads your project with static analysis (no code execution, no ts-node overhead) and produces a pre-filled docs file for every controller:
npx nestjs-docfy generate
The generated file comes with inferred summaries, response types, and common error responses already in place. You edit from there instead of starting from scratch.
It's idempotent by default, running it again won't touch files that already exist. When you add a new endpoint and want to merge only the new method block without losing your existing edits:
npx nestjs-docfy generate --force
The CLI auto-detects your project layout, so monorepos (Nx, Nest CLI, generic packages/ or apps/ structures) work without any configuration.
There's also a --dry-run flag if you want to preview output before writing anything to disk.
A check command for CI
The other side of the workflow is keeping docs in sync as the codebase evolves. The check command exits with code 1 if any controller has undocumented methods or no companion file at all:
npx nestjs-docfy check
Output looks like this when something is out of sync:
✖ UsersController, undocumented methods: updateProfile, deleteAccount
→ run nestjs-docfy generate --force to merge new methods
✖ 2 controller(s) out of sync.
Drop it into your pipeline and docs drift gets caught before it reaches main.
Type-safe method keys
The docs() function now enforces that every key in config.methods actually exists on the controller class. Typos are a compile error, not a silent runtime warning:
docs(UsersController, {
methods: {
findAll: [...], // ✔ exists on UsersController
typoMethod: [...], // ✖ TypeScript error
},
});
Interface-typed DTOs just work
This one came up a lot. If your response or body type is a TypeScript interface, Swagger can't use it as a type: value because interfaces are erased at runtime. Previously you had to convert them to classes or write the schema by hand.
Now the CLI detects this automatically and generates an inline schema: object instead:
// Your existing interface — no changes needed
export interface RegisterResponseDto {
success: boolean;
message: string | null;
}
Generated output:
ApiResponse({
status: 201,
description: 'Created',
schema: {
type: 'object',
properties: {
success: { type: 'boolean' },
message: { type: 'string', nullable: true },
},
required: ['success'],
},
}),
Supports primitives, nullable unions, arrays, nested interfaces, and optional properties.
class-validator inference
If a DTO uses class-validator decorators and doesn't already have @ApiProperty on its properties, the CLI infers the full JSON Schema from the validators — no manual annotation needed:
export class CreateUserDto {
@IsString()
@MinLength(2)
name: string;
@IsEmail()
email: string;
@IsOptional()
@IsString()
bio?: string;
}
Generated output:
ApiBody({
schema: {
type: 'object',
properties: {
name: { type: 'string', minLength: 2 },
email: { type: 'string', format: 'email' },
bio: { type: 'string' },
},
required: ['name', 'email'],
},
}),
If any property already has @ApiProperty, inference is skipped entirely and type: ClassName is used instead, existing annotations are never overwritten.
@HttpCode() awareness
The CLI reads @HttpCode() on route handlers and uses the correct status code in the generated ApiResponse. A @Post with @HttpCode(204) gets status: 204, not the default 201.
The core idea is still the same — controllers express behavior, docs files express documentation, but now the friction of setting that up and keeping it current is mostly gone.
🔗 nestjs-docfy on GitHub · 📦 npm package
If you were holding off because of the manual setup, this release should make it a lot more practical.
Top comments (0)