Skip to main content

Swagger Documentation

Overview

The starter kit integrates Swagger (OpenAPI) to provide interactive API documentation that's always in sync with your codebase. This makes it easier for developers to understand, test, and consume your APIs.

Swagger Setup

The starter kit configures Swagger using the @nestjs/swagger package:

// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
const app = await NestFactory.create(AppModule);

// Setup validation pipe
app.useGlobalPipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
);

// Setup Swagger
const config = new DocumentBuilder()
.setTitle('NestJS Starter API')
.setDescription('API documentation for the NestJS Starter Kit')
.setVersion('1.0')
.addBearerAuth()
.addTag('auth', 'Authentication endpoints')
.addTag('users', 'User management endpoints')
.build();

const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api-docs', app, document);

await app.listen(3000);
}
bootstrap();

Accessing Swagger UI

Once your application is running, you can access the Swagger UI at:

http://localhost:3000/api-docs

This provides an interactive interface where you can:

  • Browse all available endpoints
  • View request/response schemas
  • Execute API calls directly from the browser
  • View authentication requirements

Documenting Controllers and DTOs

Controller Documentation

import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBearerAuth,
ApiBody,
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './entities/user.entity';

@ApiTags('users')
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}

@Post()
@ApiOperation({ summary: 'Create new user' })
@ApiResponse({
status: 201,
description: 'User has been successfully created',
type: User,
})
@ApiResponse({ status: 400, description: 'Bad request' })
@ApiBody({ type: CreateUserDto })
async create(@Body() createUserDto: CreateUserDto): Promise<User> {
return this.usersService.create(createUserDto);
}

@Get()
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Get all users' })
@ApiResponse({
status: 200,
description: 'Returns all users',
type: [User],
})
@ApiResponse({ status: 401, description: 'Unauthorized' })
findAll() {
return this.usersService.findAll();
}
}

DTO Documentation

import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import {
IsEmail,
IsString,
MinLength,
IsOptional,
IsBoolean,
} from 'class-validator';

export class CreateUserDto {
@ApiProperty({
example: 'user@example.com',
description: 'The email of the user',
})
@IsEmail()
email: string;

@ApiProperty({
example: 'StrongP@ssw0rd',
description: 'The password of the user',
minLength: 8,
})
@IsString()
@MinLength(8)
password: string;

@ApiProperty({
example: 'John',
description: 'The first name of the user',
})
@IsString()
firstName: string;

@ApiProperty({
example: 'Doe',
description: 'The last name of the user',
})
@IsString()
lastName: string;

@ApiPropertyOptional({
example: true,
description: 'Whether the user is active',
default: false,
})
@IsOptional()
@IsBoolean()
isActive?: boolean;
}

Entity Documentation

import { ApiProperty } from '@nestjs/swagger';
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';

@Entity('users')
export class User {
@ApiProperty({
example: '123e4567-e89b-12d3-a456-426614174000',
description: 'The unique identifier of the user',
})
@PrimaryGeneratedColumn('uuid')
id: string;

@ApiProperty({
example: 'user@example.com',
description: 'The email of the user',
})
@Column({ unique: true })
email: string;

@ApiProperty({
example: 'John',
description: 'The first name of the user',
})
@Column()
firstName: string;

@ApiProperty({
example: 'Doe',
description: 'The last name of the user',
})
@Column()
lastName: string;

@ApiProperty({
example: false,
description: 'Whether the user is active',
})
@Column({ default: false })
isActive: boolean;

@ApiProperty({
example: '2023-01-01T00:00:00Z',
description: 'The date when the user was created',
})
@CreateDateColumn()
createdAt: Date;

@ApiProperty({
example: '2023-01-01T00:00:00Z',
description: 'The date when the user was last updated',
})
@UpdateDateColumn()
updatedAt: Date;

// Note: Password field is excluded from Swagger docs for security
@Column()
password: string;
}

Authentication Documentation

Configuring Authentication in Swagger

// Add Bearer Authentication support
const config = new DocumentBuilder()
// ... other configuration
.addBearerAuth(
{
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
in: 'header',
},
'access-token', // This is a reference name used to tag endpoints
)
.build();

Securing Endpoints in Swagger

@Get('profile')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth('access-token')
@ApiOperation({ summary: 'Get user profile' })
@ApiResponse({
status: 200,
description: 'Returns the user profile',
type: User,
})
@ApiResponse({ status: 401, description: 'Unauthorized' })
getProfile(@Request() req) {
return req.user;
}

Advanced Swagger Configuration

Custom Response Schema

export class PaginatedUsersResponse {
@ApiProperty({ type: [User] })
data: User[];

@ApiProperty({
type: 'object',
example: {
total: 100,
limit: 10,
offset: 0,
},
})
meta: {
total: number;
limit: number;
offset: number;
};
}

@Get()
@ApiResponse({
status: 200,
description: 'Returns paginated users',
type: PaginatedUsersResponse,
})
async findAll(@Query() paginationDto: PaginationDto): Promise<PaginatedUsersResponse> {
// Implementation
}

Grouping Endpoints

// Group endpoints by functionality
@ApiTags('authentication')
@Controller('auth')
export class AuthController {
// Authentication endpoints
}

@ApiTags('user-management')
@Controller('users')
export class UsersController {
// User management endpoints
}

@ApiTags('user-management')
@Controller('roles')
export class RolesController {
// Roles management endpoints (same tag for grouping)
}

Swagger Plugin

You can also enable the Swagger plugin in your nest-cli.json for automatic documentation:

{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": [
{
"name": "@nestjs/swagger",
"options": {
"classValidatorShim": true,
"introspectComments": true
}
}
]
}
}

Security Considerations

Securing Swagger UI in Production

For production environments, you might want to secure the Swagger UI:

if (process.env.NODE_ENV !== 'production') {
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api-docs', app, document);
} else {
// In production, add password protection or disable Swagger
const document = SwaggerModule.createDocument(app, config);

// Option 1: Password protection
app.use('/api-docs', (req, res, next) => {
const auth = { login: process.env.SWAGGER_USER, password: process.env.SWAGGER_PASSWORD };
const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':');

if (login && password && login === auth.login && password === auth.password) {
return next();
}

res.set('WWW-Authenticate', 'Basic realm="Swagger Documentation"');
res.status(401).send('Authentication required.');
});

SwaggerModule.setup('api-docs', app, document);

// Option 2: Disable Swagger completely
// Do nothing, which makes Swagger unavailable
}

Hiding Sensitive Information

Use the @ApiHideProperty() decorator to hide sensitive fields from the Swagger documentation:

import { ApiHideProperty } from '@nestjs/swagger';

export class User {
// Visible fields...

@ApiHideProperty()
@Column()
password: string;

@ApiHideProperty()
@Column({ nullable: true })
refreshToken: string;
}

Best Practices

  1. Keep Documentation Updated: Update Swagger annotations when you change your API
  2. Use Tags Consistently: Organize endpoints with consistent tagging
  3. Document Error Responses: Include all possible response status codes
  4. Add Examples: Include example values for better understanding
  5. Document Authentication: Clearly indicate which endpoints require authentication
  6. Use Operation IDs: Add unique operation IDs for easier API client generation
  7. Include Descriptions: Add detailed descriptions to complex endpoints and parameters