DEV Community

Dilum Darshana
Dilum Darshana

Posted on

API Documentation for NestJS

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
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

Basically, that's it. http://localhost:3000/api-docs will be showing the elegant documentation structure with separate sections.

Swagger 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) {}

}
Enter fullscreen mode Exit fullscreen mode

Custom section name

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;
}
Enter fullscreen mode Exit fullscreen mode

Example and schema

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;
}
Enter fullscreen mode Exit fullscreen mode

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,
    };
}
Enter fullscreen mode Exit fullscreen mode

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,
    };
 }
Enter fullscreen mode Exit fullscreen mode

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,
  };
}
Enter fullscreen mode Exit fullscreen mode

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)