Introduction
API documentation is essential for developers consuming your API. OpenAPI (formerly Swagger) provides a standard format for describing REST APIs. NestJS integrates seamlessly with Swagger to auto-generate documentation from your code.
Key Concepts
- OpenAPI: Specification for describing REST APIs
- Swagger UI: Interactive documentation interface
- Decorators: NestJS decorators that add OpenAPI metadata
- Schema Generation: Automatic DTO-to-schema conversion
Real World Context
Swagger documentation enables:
- Developer onboarding without reading code
- API testing directly in the browser
- Client SDK generation
- Contract-first API development
Deep Dive
Installation & Setup
Install the Swagger package and configure the document builder in your bootstrap function.
bashnpm install @nestjs/swagger
Use DocumentBuilder to set metadata, then call SwaggerModule.setup() to mount the Swagger UI.
typescriptimport { 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('The API documentation') .setVersion('1.0') .addBearerAuth() .addTag('users') .addTag('products') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document); await app.listen(3000); } bootstrap();
After starting the server, the interactive Swagger UI is available at http://localhost:3000/api.
Controller Documentation
Decorate controllers with @ApiTags() for grouping and methods with @ApiOperation() and @ApiResponse() for endpoint details.
typescriptimport { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger'; @ApiTags('users') @Controller('users') export class UsersController { @Get() @ApiOperation({ summary: 'Get all users' }) @ApiResponse({ status: 200, description: 'Returns all users', type: [UserDto] }) findAll(): Promise<UserDto[]> { return this.usersService.findAll(); } @Get(':id') @ApiOperation({ summary: 'Get user by ID' }) @ApiResponse({ status: 200, description: 'Returns the user', type: UserDto }) @ApiResponse({ status: 404, description: 'User not found' }) findOne(@Param('id') id: string): Promise<UserDto> { return this.usersService.findOne(id); } @Post() @ApiBearerAuth() @ApiOperation({ summary: 'Create a new user' }) @ApiResponse({ status: 201, description: 'User created', type: UserDto }) @ApiResponse({ status: 400, description: 'Invalid input' }) create(@Body() createUserDto: CreateUserDto): Promise<UserDto> { return this.usersService.create(createUserDto); } }
Passing type: [UserDto] (array syntax) tells Swagger the response is an array of that schema.
DTO Documentation
Use @ApiProperty() on each field to specify descriptions, examples, and constraints that appear in the generated schema.
typescriptimport { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; export class CreateUserDto { @ApiProperty({ description: 'User email address', example: 'user@example.com', }) @IsEmail() email: string; @ApiProperty({ description: 'User full name', minLength: 2, maxLength: 100, example: 'John Doe', }) @IsString() @MinLength(2) name: string; @ApiPropertyOptional({ description: 'User age', minimum: 0, maximum: 120, example: 25, }) @IsOptional() @IsInt() age?: number; }
Use @ApiPropertyOptional() instead of @ApiProperty({ required: false }) for cleaner syntax on optional fields.
CLI Plugin for Auto-Generation
Enable the @nestjs/swagger CLI plugin to infer @ApiProperty() metadata automatically from TypeScript types and JSDoc comments.
json// nest-cli.json { "compilerOptions": { "plugins": [ { "name": "@nestjs/swagger", "options": { "classValidatorShim": true, "introspectComments": true } } ] } }
With the plugin, decorators are optional—metadata is inferred:
typescriptexport class CreateUserDto { /** User email address */ @IsEmail() email: string; // ApiProperty auto-generated /** User full name */ @IsString() name: string; }
JSDoc comments become the description field in the generated schema, reducing decorator boilerplate significantly.
Common Pitfalls
- Missing return types: Without types, Swagger can't generate schemas.
- Forgetting ApiProperty on nested objects: Nested DTOs need decorators too.
- Not documenting errors: Document 400, 401, 404 responses.
Best Practices
- Use the CLI plugin to reduce decorator boilerplate
- Document all response types including errors
- Add examples to ApiProperty for clarity
- Group related endpoints with ApiTags
- Keep documentation in sync with code
Summary
@nestjs/swagger integrates OpenAPI documentation into NestJS. Use decorators like @ApiOperation, @ApiResponse, and @ApiProperty to document endpoints. The CLI plugin can auto-generate much of the documentation from types.
Code Examples
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('The API documentation')
.setVersion('1.0')
.addBearerAuth()
.addTag('users')
.addTag('products')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();