Julio César

Desarrollador de software, con experiencia en desarrollo web y móvil. Apasionado por la tecnología y la innovación.

Documentación automática con Swagger en NestJS

Contenido

Instalación de dependencias

El primer paso que debemos ejecutar es la instalación de el paquete swagger en nuestro proyecto, si usamos npm ejecutamos la siguiente instrucción:

npm install --save @nestjs/swagger

En caso de usar yarn ejecutamos la siguiente acción:

yarn add @nestjs/swagger

Configuración del proyecto

Una vez instalada el paquete swagger vamos a ir al archivo main.ts y agregamos la siguiente configuración:

import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

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

  const config = new DocumentBuilder()
    .setTitle('Blog API')
    .setDescription('The blog API description')
    .setVersion('1.0')
    .addTag('blog')
    .build();
  const documentFactory = () => SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('docs', app, documentFactory, {
    jsonDocumentUrl: '/swagger-json',
  });

  ...
}
await bootstrap();

En el fragmento anterior agregamos el módulo SwaggerModule a nuestra aplicación y al mismo tiempo creamos un documento de OpenAPI que puede ser accedido en la ruta docs. Adicionalmente, configuramos la ruta swagger-json para exponer la documentación generada por Swagger en formato JSON para que pueda ser consumido por modelos de IA o herramientas automatizadas.

Documentación de DTO

Para documentar los atributos de un DTO tenemos algunos decoradores muy útiles, como, por ejemplo:

Estos decoradores vienen del paquete @nestjs/swagger. Como el nombre indica uno se usa cuando la propiedad es requerida y el otro cuando es opcional.

Estos decoradores reciben un objeto de configuración que puede contener los siguientes atributos:

Entre otras posibles configuraciones, es posible profundizar más en estos decoradores en la documentación oficial de NestJS para Swagger.

Para los DTO de tipo Update, aquellos que se derivan de DTOs definidos completamente a los que se aplica el mapped type PartialType, debemos mantenerlos como fueron definidos, pero ahora el paquete se debe importar desde @nestjs/swagger que se encarga de agregar el decorador @ApiPropertyOptional() internamente.

Un ejemplo de un UpdateDTO se muestra a continuación:

import { PartialType } from "@nestjs/swagger";
import { CreatePostDto } from "./create-post.dto";

export class UpdatePostDto extends PartialType(CreatePostDto) {}

Documentación de entidades de base de datos

Documentación de endpoints

Como ya sabemos los endpoints son gestionados desde los controladores, por lo tanto, vamos a ver los decoradores y parámetros que debemos usar en un controller con el fin de generar una buena documentación.

Descripción del endpoint

Para un endpoint podemos hacer uso del decorador @ApiOperation() y pasar los siguientes atributos en el objeto de configuración:

Un ejemplo de los anterior es:

 @ApiOperation({
    summary: 'Get all users',
    description: 'Retrieve a list of all users registered in the system',
    deprecated: true
  })
  @Get()
  getUsers() {
    return this.userService.getUsers();
  }

Describir query params

Describir path params

Descripción de las respuestas

Podemos describir posibles respuestas de nuestro endpoint mediante el decorador @ApiResponse() que puede recibir los siguiente atributos en su objeto de configuración:

Un ejemplo de la descripción de respuestas se muestra a continuación:

export class UsersController {
  @Get()
  @ApiResponse({
    status: 200,
    description: "The users have been successfully retrieved.",
    type: UserDto,
    isArray: true,
  })
  findAll() {
    return [];
  }
}

También es importante mencionar que NestJS nos ofrece una gran lista de API responses predefinidas que nos pueden ahorrar mucho tiempo, una parte de la lista se muestra a continuación:

Un ejemplo que muestra el uso de los decoradores predefinidos se muestra a continuación:

@Post()
@ApiCreatedResponse({ description: 'The record has been successfully created.'})
@ApiForbiddenResponse({ description: 'Forbidden.'})
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

Seguridad de la documentación

To document query and route parameters in NestJS, you use the @ApiParam() and @ApiQuery() decorators from the @nestjs/swagger package.Here is a complete example showing how to implement both alongside your route handlers:

import { Controller, Get, Param, Query } from ‘@nestjs/common’; import { ApiOperation, ApiParam, ApiQuery, ApiTags } from ‘@nestjs/swagger’;

@ApiTags(‘products’) @Controller(‘products’) export class ProductsController {

@Get(‘:id’) @ApiOperation({ summary: ‘Get a product by ID’ }) // 1. Documenting a Route/Path Parameter @ApiParam({ name: ‘id’, type: String, description: ‘The unique identifier of the product’, example: ‘prod_95x82103’, }) findOne(@Param(‘id’) id: string) { return This action returns product #${id}; }

@Get() @ApiOperation({ summary: ‘List all products with pagination’ }) // 2. Documenting individual Query Parameters @ApiQuery({ name: ‘limit’, type: Number, required: false, description: ‘Number of items to return per page’, example: 10, }) @ApiQuery({ name: ‘search’, type: String, required: false, description: ‘Filter products by name or description’, }) findAll(@Query(‘limit’) limit?: number, @Query(‘search’) search?: string) { return Returns products filtered by "${search}", limited to ${limit} items.; } }

Pro-Tip: Documenting a Query DTOIf you have a lot of query parameters, it is much cleaner to group them into a class (Data Transfer Object) and use the @ApiProperty() decorator inside that class. Swagger will automatically pick them up without cluttering your controller:

// products-query.dto.ts import { ApiPropertyOptional } from ‘@nestjs/swagger’;

export class ProductsQueryDto { @ApiPropertyOptional({ description: ‘Number of items to return’, example: 10 }) limit?: number;

@ApiPropertyOptional({ description: ‘Filter products by name’ }) search?: string; }

// In your controller: @Get() findAll(@Query() query: ProductsQueryDto) { // Swagger automatically documents ‘limit’ and ‘search’ here! }