This time we are going to learn about how to create API documentation for NestJS application using Swagger. There are many ways in the world, let's focus on Swagger which is NestJS official preference.
First, install the dependency,
$ pnpm add @nestjs/swagger
Then, initialise the Swagger in bootstrap which is main.ts file,
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
async function bootstrap() {
// Set up Swagger documentation for the API
const swaggerConfig = new DocumentBuilder()
.setTitle('Taskify API documentation')
.setDescription('Taskify API documentation')
.setVersion('1.0')
.addTag('api')
.build();
const documentFactory = SwaggerModule.createDocument(app, swaggerConfig);
SwaggerModule.setup('api-docs', app, documentFactory);
// Start the application and listen on the specified port
await app.listen(port || 3000);
}
Basically, that's it. http://localhost:3000/api-docs will be showing the elegant documentation structure with separate sections.
Sections are automatically split based on the controller name. It is possible to give a custom name though. Use ApiTags
decorator
import { ApiTags } from '@nestjs/swagger';
@ApiTags('Task APIs')
@Controller('task')
export class TaskController {
constructor(private readonly taskService: TaskService) {}
}
By default, the API definitions are empty. We need to improve further. Let's do it.
The schema and example section take from the DTO file. Im my example, create task API and createTaskDTO which is validate the task create inputs. We can use this file to improve the documentation using ApiProperty decorator.
// create-task.dto.ts file
import { Priority, TaskStatus } from '@prisma/client';
import { IsNotEmpty, IsString } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class CreateTaskDto {
@ApiProperty({
description: 'Title of the task',
example: 'Complete the project report',
})
@IsString()
@IsNotEmpty()
title: string;
@ApiProperty({
description: 'Description of the task',
example: 'Write a detailed report on the project progress',
})
@IsString()
description: string;
@ApiProperty({
description: 'Priority level of the task',
example: 'HIGH',
enum: Priority,
})
@IsString()
@IsNotEmpty()
priority: Priority;
@ApiProperty({
description: 'Status of the task',
example: 'TODO',
enum: TaskStatus,
})
@IsString()
@IsNotEmpty()
status: TaskStatus;
}
Then, the response section in the API can be filled with ApiResponse decorator from the controller file. Note that the CreateTaskResponseDto type. This should be a DTO for the response. Let's modify the create-task.dto file for that,
// create-task.dto.ts
export class CreateTaskResponseDto {
@ApiProperty()
id: string;
@ApiProperty()
title: string;
@ApiProperty()
description: string;
@ApiProperty()
priority: Priority;
@ApiProperty()
status: TaskStatus;
}
Now, need to call the ApiResponse decorator from the controller
// task.controller.ts file
@ApiResponse({ status: HttpStatus.CREATED, description: 'Task created successfully', type: CreateTaskResponseDto }) // Swagger response documentation.
@HttpCode(HttpStatus.CREATED) // Sets the HTTP status code to 201 (Created).
@UseInterceptors(ResponseInterceptor) // Intercepts the response to format it consistently.
@Post() // Handles POST requests to the /task endpoint.
async createTask(@Body() createTaskDto: CreateTaskDto, @CurrentUser() user: User) {
const task = await this.taskService.create(createTaskDto, user.id);
return {
message: 'Task created successfully',
data: task,
};
}
Additionally, if we want to add description for each API, can be used ApiOperation decorator from the controller. Example,
@ApiOperation({ summary: 'Some description about this API' }) // Swagger documentation for the endpoint.
@ApiResponse({ status: HttpStatus.CREATED, description: 'Task created successfully', type: CreateTaskResponseDto }) // Swagger response documentation.
@HttpCode(HttpStatus.CREATED) // Sets the HTTP status code to 201 (Created).
@UseInterceptors(ResponseInterceptor) // Intercepts the response to format it consistently.
@Post() // Handles POST requests to the /task endpoint.
async createTask(@Body() createTaskDto: CreateTaskDto, @CurrentUser() user: User) {
const task = await this.taskService.create(createTaskDto, user.id);
return {
message: 'Task created successfully',
data: task,
};
}
It is possible to add multiple response types, example error responses. Just use the different decorators as bellow,
@ApiOperation({ summary: 'Create a new task' }) // Swagger documentation for the endpoint.
@ApiCreatedResponse({
description: 'Task created successfully',
type: CreateTaskResponseDto,
}) // Swagger response documentation.
@ApiBadRequestResponse({ description: 'Bad Request' }) // Swagger error response documentation.
@ApiUnauthorizedResponse({ description: 'Unauthorized' }) // Swagger error response documentation.
@HttpCode(HttpStatus.CREATED) // Sets the HTTP status code to 201 (Created).
@UseInterceptors(ResponseInterceptor) // Intercepts the response to format it consistently.
@Post() // Handles POST requests to the /task endpoint.
async createTask(@Body() createTaskDto: CreateTaskDto, @CurrentUser() user: User) {
const task = await this.taskService.create(createTaskDto, user.id);
return {
message: 'Task created successfully',
data: task,
};
}
Conclusion
From my perspective, this approach allow us to create useful API documentation with minimal effort. The big advantage of having such a documentation is improve team collaboration specially for front-end teams.
Cheers... Happy coding!!!
Top comments (0)