Saltar al contenido principal

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

Configuración con Express + swagger-jsdoc

Instalación:

npm install swagger-jsdoc swagger-ui-express

Configuración inicial:

src/swagger.js
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:

src/app.js
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 $ref para los schemas. Evita duplicar definiciones y mantiene la especificación DRY.
  • Incluye examples reales. 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.yaml debe estar en el mismo repositorio Git que el código fuente.
  • Valida las peticiones contra la especificación usando express-openapi-validator para asegurar que el código y la doc estén siempre sincronizados.