Documentación de API REST
OpenAPI (anteriormente conocido como Swagger Specification) es el estándar del sector para describir APIs HTTP de forma legible tanto por humanos como por máquinas. Un fichero OpenAPI en formato YAML o JSON define de forma completa todos los endpoints de la API: rutas, métodos HTTP, parámetros, cuerpos de petición, respuestas posibles y esquemas de datos.
A partir de esa especificación se pueden generar:
- Interfaces interactivas para explorar y probar la API (Swagger UI, Redoc).
- SDKs de cliente en múltiples lenguajes.
- Tests de contrato automatizados.
Herramientas del ecosistema Node.js
swagger-jsdoc: Genera el fichero OpenAPI a partir de comentarios JSDoc en las rutas.swagger-ui-express: Sirve Swagger UI como middleware de Express.@nestjs/swagger: Decoradores OpenAPI para proyectos NestJS.redoc-express: Alternativa visual más limpia a Swagger UI.express-openapi-validator: Valida peticiones/respuestas contra la especificación.
Configuración con Express + swagger-jsdoc
Instalación:
npm install swagger-jsdoc swagger-ui-express
Configuración inicial:
import swaggerJsDoc from 'swagger-jsdoc';
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Mi API Backend',
version: '1.0.0',
description: 'Documentación generada automáticamente con swagger-jsdoc',
contact: {
name: 'Equipo de desarrollo',
email: 'dev@ejemplo.com',
},
},
servers: [
{
url: 'http://localhost:3000/api/v1',
description: 'Servidor de desarrollo',
},
{
url: 'https://api.ejemplo.com/v1',
description: 'Servidor de producción',
},
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
},
// Rutas donde swagger-jsdoc buscará los comentarios @swagger
apis: ['./src/routes/*.js'],
};
const swaggerSpec = swaggerJsDoc(options);
export default swaggerSpec;
Montar Swagger UI en Express:
import express from 'express';
import swaggerUi from 'swagger-ui-express';
import swaggerSpec from './swagger.js';
import userRoutes from './routes/users.js';
const app = express();
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
// A partir de aquí, tus rutas normales
app.use('/api/v1/users', userRoutes);
export default app;
Accede a la documentación en: http://localhost:3000/api-docs
Anatomía de un comentario OpenAPI en una ruta
Los comentarios se escriben con la etiqueta @openapi (o @swagger) dentro de bloques JSDoc:
/**
* @openapi
* /users:
* get:
* summary: Listar todos los usuarios
* description: Devuelve la lista paginada de usuarios registrados en el sistema.
* tags:
* - Usuarios
* security:
* - bearerAuth: []
* parameters:
* - in: query
* name: page
* schema:
* type: integer
* default: 1
* description: Número de página
* - in: query
* name: limit
* schema:
* type: integer
* default: 20
* description: Resultados por página (máx. 100)
* responses:
* 200:
* description: Lista de usuarios obtenida correctamente
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/UserListResponse'
* 401:
* description: Token de autenticación ausente o inválido
* 500:
* description: Error interno del servidor
*/
router.get('/', authMiddleware, userController.listUsers);
Definición de schemas reutilizables
Los schemas se suelen definir en un fichero de modelos aparte para poder referenciarlos con $ref:
/**
* @openapi
* components:
* schemas:
* User:
* type: object
* required:
* - id
* - name
* - email
* properties:
* id:
* type: string
* format: uuid
* example: "550e8400-e29b-41d4-a716-446655440000"
* name:
* type: string
* example: "Ana García"
* email:
* type: string
* format: email
* example: "ana@ejemplo.com"
* createdAt:
* type: string
* format: date-time
* example: "2024-01-15T10:30:00Z"
*
* UserListResponse:
* type: object
* properties:
* data:
* type: array
* items:
* $ref: '#/components/schemas/User'
* pagination:
* type: object
* properties:
* page:
* type: integer
* limit:
* type: integer
* total:
* type: integer
*/
Ejemplo completo: CRUD de usuarios
/**
* @openapi
* /users/{id}:
* get:
* summary: Obtener un usuario por ID
* tags: [Usuarios]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* format: uuid
* description: ID único del usuario
* responses:
* 200:
* description: Usuario encontrado
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 404:
* description: Usuario no encontrado
*
* put:
* summary: Actualizar un usuario
* tags: [Usuarios]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* email:
* type: string
* format: email
* responses:
* 200:
* description: Usuario actualizado correctamente
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/User'
* 400:
* description: Datos de entrada inválidos
* 404:
* description: Usuario no encontrado
*
* delete:
* summary: Eliminar un usuario
* tags: [Usuarios]
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* responses:
* 204:
* description: Usuario eliminado correctamente
* 404:
* description: Usuario no encontrado
*/
router.get('/:id', authMiddleware, userController.getUser);
router.put('/:id', authMiddleware, userController.updateUser);
router.delete('/:id', authMiddleware, userController.deleteUser);
Alternativa: fichero YAML centralizado
En proyectos grandes puede ser preferible mantener la especificación en un único fichero docs/openapi.yaml en lugar de dispersarla en comentarios:
# docs/openapi.yaml
openapi: 3.0.0
info:
title: Mi API Backend
version: 1.0.0
paths:
/users:
get:
summary: Listar usuarios
tags:
- Usuarios
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
components:
schemas:
User:
type: object
properties:
id:
type: string
name:
type: string
Y se carga directamente en Express:
import YAML from 'yamljs';
import swaggerUi from 'swagger-ui-express';
const swaggerDocument = YAML.load('./docs/openapi.yaml');
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
Buenas prácticas
- Agrupa los endpoints con
tags. Permite que Swagger UI muestre los endpoints organizados por recurso (Usuarios, Productos, Pedidos…). - Documenta todos los códigos de error posibles. El cliente necesita saber qué esperar en los casos 400, 401, 403, 404 y 500.
- Usa
$refpara los schemas. Evita duplicar definiciones y mantiene la especificación DRY. - Incluye
examplesreales. Los ejemplos de petición y respuesta hacen la documentación mucho más útil. - Versiona la especificación con el código. El fichero
openapi.yamldebe estar en el mismo repositorio Git que el código fuente. - Valida las peticiones contra la especificación usando
express-openapi-validatorpara asegurar que el código y la doc estén siempre sincronizados.