En esta página
Swagger y documentación de API
¿Por qué documentar tu API con Swagger?
Una API sin documentación es una API que nadie puede usar eficientemente. Swagger (especificación OpenAPI) proporciona:
- Documentación interactiva: Los desarrolladores pueden explorar y probar los endpoints directamente en el navegador
- Generación de clientes: A partir de la especificación se pueden generar clientes SDK para múltiples lenguajes
- Contrato de API: La especificación actúa como contrato entre el equipo de backend y frontend
- Testing: Permite probar endpoints manualmente durante el desarrollo
Instalación
npm install @nestjs/swagger swagger-ui-expressSi usas Fastify en lugar de Express:
npm install @nestjs/swagger @fastify/staticDocumentando entidades de TypeORM
Para que las entidades aparezcan correctamente como modelos en Swagger, úsalas con @ApiProperty:
// productos/entities/producto.entity.ts
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
@Entity('productos')
export class Producto {
@ApiProperty({ description: 'ID único del producto', example: 'uuid-aqui' })
@PrimaryGeneratedColumn('uuid')
id: string;
@ApiProperty({ example: 'Laptop Gaming Pro' })
@Column()
nombre: string;
@ApiPropertyOptional({ example: 'Descripción opcional' })
@Column({ nullable: true })
descripcion: string | null;
@ApiProperty({ example: 1299.99 })
@Column({ type: 'decimal' })
precio: number;
@ApiProperty({ example: '2024-01-15T10:30:00.000Z' })
@CreateDateColumn()
creadoEn: Date;
}Registrando modelos extra en la especificación
Para que los modelos de respuesta complejos aparezcan en el esquema de Swagger, debes extraModels:
const documento = SwaggerModule.createDocument(app, config, {
extraModels: [ResultadoPaginado, ErrorResponse, Producto, Usuario],
});Documentando la autenticación
// auth/auth.controller.ts — con decoradores de Swagger
import { ApiTags, ApiOperation, ApiBody, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger';
class LoginResponseDto {
@ApiProperty({ description: 'Token JWT de acceso', example: 'eyJhbGci...' })
accessToken: string;
}
@ApiTags('Auth')
@Controller('auth')
export class AuthController {
@Post('login')
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Iniciar sesión',
description: 'Autentica al usuario y retorna un token JWT de acceso',
})
@ApiBody({
schema: {
type: 'object',
required: ['email', 'password'],
properties: {
email: { type: 'string', format: 'email', example: '[email protected]' },
password: { type: 'string', minLength: 8, example: 'MiPassword1!' },
},
},
})
@ApiOkResponse({ type: LoginResponseDto, description: 'Login exitoso' })
@ApiUnauthorizedException({ description: 'Credenciales incorrectas' })
login(@Body() loginDto: LoginDto) {
return this.authService.login(loginDto);
}
}Grouping con tags y versiones
// Organiza los endpoints en grupos lógicos con tags
@ApiTags('Administración — Usuarios')
@Controller('admin/usuarios')
export class AdminUsuariosController {}
@ApiTags('Administración — Configuración')
@Controller('admin/config')
export class AdminConfigController {}Exportar la especificación OpenAPI como JSON
Para CI/CD o para generar clientes, puedes guardar la especificación como archivo:
// scripts/generate-swagger.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from '../src/app.module';
import * as fs from 'fs';
async function generateSwagger() {
const app = await NestFactory.create(AppModule, { logger: false });
const config = new DocumentBuilder()
.setTitle('Mi API')
.setVersion('1.0')
.addBearerAuth()
.build();
const documento = SwaggerModule.createDocument(app, config);
fs.writeFileSync(
'./openapi.json',
JSON.stringify(documento, null, 2),
'utf-8'
);
await app.close();
console.log('Especificación OpenAPI generada en openapi.json');
}
generateSwagger();Deshabilitar Swagger en producción
Es una buena práctica no exponer la documentación en producción:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
if (process.env['NODE_ENV'] !== 'production') {
const config = new DocumentBuilder()
.setTitle('Mi API')
.setVersion('1.0')
.addBearerAuth()
.build();
const documento = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, documento);
}
await app.listen(3000);
}Documentando modelos de respuesta paginada
// common/dto/paginated-response.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { Type } from '@nestjs/common';
export function PaginatedResponseDto<T>(ItemClass: Type<T>) {
class PaginatedResponseDtoClass {
@ApiProperty({ isArray: true, type: () => ItemClass })
datos: T[];
@ApiProperty({ example: 100 })
total: number;
@ApiProperty({ example: 1 })
pagina: number;
@ApiProperty({ example: 10 })
totalPaginas: number;
@ApiProperty({ example: true })
tieneSiguiente: boolean;
}
return PaginatedResponseDtoClass;
}
// Uso en el controlador
class ProductosPaginadosResponse extends PaginatedResponseDto(Producto) {}
@ApiOkResponse({ type: ProductosPaginadosResponse })
findAll() {}Con Swagger configurado correctamente, tu API está completamente documentada y lista para ser consumida por cualquier equipo. En la próxima lección aprenderemos a escribir tests robustos para garantizar que toda esta funcionalidad funciona correctamente.
Inicia sesión para guardar tu progreso