Mejores prácticas para el desarrollo de APIs RESTful
Guía empresarial completa de diseño de APIs RESTful incluyendo HATEOAS, versionado, rate limiting, caching y patrones usados por Stripe, Twilio y GitHub.
Introducción
Las APIs RESTful se han convertido en el estándar de facto para la comunicación entre servicios web. Empresas como Stripe, Twilio, GitHub y Shopify han construido APIs que procesan miles de millones de requests diariamente. En este artículo, exploraremos las mejores prácticas empresariales que te ayudarán a diseñar APIs robustas, escalables y fáciles de mantener.
1. Usa sustantivos para los recursos
Los endpoints deben representar recursos (sustantivos) en lugar de acciones (verbos). Los métodos HTTP ya indican la acción a realizar.
// ✅ Correcto
GET /usuarios
POST /usuarios
GET /usuarios/123
PUT /usuarios/123
PATCH /usuarios/123
DELETE /usuarios/123
GET /usuarios/123/pedidos
POST /usuarios/123/pedidos
// ❌ Incorrecto
GET /obtenerUsuarios
POST /crearUsuario
GET /obtenerUsuario/123
POST /actualizarUsuario/123
POST /eliminarUsuario/123
2. Utiliza los métodos HTTP correctamente
Cada método HTTP tiene un propósito específico y semántica bien definida:
GET - Obtener recursos
GET /usuarios/123
- Idempotente: Múltiples requests producen el mismo resultado
- Sin efectos secundarios: No modifica datos
- Cacheable: Puede ser cacheado
POST - Crear recursos
POST /usuarios
Content-Type: application/json
{
"nombre": "Juan",
"email": "juan@example.com"
}
Response: 201 Created
Location: /usuarios/456
- No idempotente: Múltiples requests crean múltiples recursos
- Retorna 201 Created con header
Location
PUT - Reemplazar recurso completo
PUT /usuarios/123
Content-Type: application/json
{
"nombre": "Juan Actualizado",
"email": "juan.nuevo@example.com",
"edad": 30
}
- Idempotente: Múltiples requests producen el mismo resultado
- Reemplaza el recurso completo
PATCH - Actualizar parcialmente
PATCH /usuarios/123
Content-Type: application/json
{
"email": "juan.nuevo@example.com"
}
- Idempotente: Múltiples requests producen el mismo resultado
- Actualiza solo los campos especificados
DELETE - Eliminar recurso
DELETE /usuarios/123
Response: 204 No Content
- Idempotente: Múltiples requests producen el mismo resultado
3. Versionado de API
Es fundamental versionar tu API desde el principio para permitir cambios sin romper la compatibilidad con clientes existentes.
Opción 1: En la URL (Recomendado)
https://api.ejemplo.com/v1/usuarios
https://api.ejemplo.com/v2/usuarios
Ventajas:
- Fácil de entender y debuggear
- Visible en logs
- Usado por Stripe, Twilio, GitHub
Opción 2: En el header
GET /usuarios
Accept: application/vnd.ejemplo.v1+json
Opción 3: Query parameter
GET /usuarios?version=1
Estrategia de deprecación:
{
"deprecation": "2026-01-01",
"sunset": "2026-06-01",
"link": "https://api.ejemplo.com/docs/migration-v2"
}
4. Códigos de estado HTTP apropiados
Utiliza los códigos de estado HTTP correctos para comunicar el resultado de las operaciones:
2xx Success
- 200 OK: Solicitud exitosa (GET, PUT, PATCH)
- 201 Created: Recurso creado exitosamente (POST)
- 202 Accepted: Solicitud aceptada para procesamiento asíncrono
- 204 No Content: Éxito sin contenido en respuesta (DELETE)
4xx Client Errors
- 400 Bad Request: Error en la solicitud del cliente
- 401 Unauthorized: Autenticación requerida o inválida
- 403 Forbidden: Autenticado pero sin permisos
- 404 Not Found: Recurso no encontrado
- 409 Conflict: Conflicto con el estado actual (ej: email duplicado)
- 422 Unprocessable Entity: Validación falló
- 429 Too Many Requests: Rate limit excedido
5xx Server Errors
- 500 Internal Server Error: Error genérico del servidor
- 502 Bad Gateway: Error en servicio upstream
- 503 Service Unavailable: Servicio temporalmente no disponible
- 504 Gateway Timeout: Timeout en servicio upstream
5. Manejo de errores estandarizado (RFC 7807)
Usa el estándar RFC 7807 Problem Details para errores consistentes.
{
"type": "https://api.ejemplo.com/errors/validation-error",
"title": "Error de validación",
"status": 422,
"detail": "El email ya está registrado",
"instance": "/usuarios",
"errors": [
{
"field": "email",
"message": "El email juan@example.com ya existe",
"code": "EMAIL_DUPLICADO"
}
],
"timestamp": "2025-09-10T10:30:00Z",
"requestId": "abc-123-def"
}
6. Paginación, filtrado y ordenamiento
Para colecciones grandes, implementa paginación, filtrado y ordenamiento.
Paginación cursor-based (Recomendado para grandes datasets)
GET /usuarios?limit=20&cursor=eyJpZCI6MTIzfQ==
Response:
{
"data": [...],
"pagination": {
"nextCursor": "eyJpZCI6MTQzfQ==",
"prevCursor": "eyJpZCI6MTAzfQ==",
"hasMore": true
}
}
Paginación offset-based
GET /usuarios?page=2&limit=20&sort=createdAt&order=desc
Response:
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 1000,
"totalPages": 50
}
}
Filtrado
GET /productos?categoria=electronica&precio_min=100&precio_max=500&disponible=true
Ordenamiento
GET /usuarios?sort=nombre,createdAt&order=asc,desc
7. Rate Limiting y Throttling
Protege tu API de abuso y asegura disponibilidad para todos los clientes.
Response Headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1694361600
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
Estrategias:
// Token Bucket Algorithm
const rateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100, // 100 requests por ventana
standardHeaders: true,
legacyHeaders: false,
handler: (req, res) => {
res.status(429).json({
type: "https://api.ejemplo.com/errors/rate-limit",
title: "Rate limit excedido",
status: 429,
detail: "Has excedido el límite de 100 requests por 15 minutos",
retryAfter: 900
});
}
});
// Rate limiting por usuario/API key
const userRateLimiter = rateLimit({
keyGenerator: (req) => req.user.id,
windowMs: 60 * 1000,
max: async (req) => {
// Diferentes límites por plan
const user = await User.findById(req.user.id);
return user.plan === 'premium' ? 1000 : 100;
}
});
8. Caching estratégico
Implementa caching para mejorar rendimiento y reducir carga del servidor.
ETags
GET /usuarios/123
Response:
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Cache-Control: max-age=3600
---
GET /usuarios/123
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Response:
HTTP/1.1 304 Not Modified
Cache-Control
Cache-Control: public, max-age=3600 # Cacheable por 1 hora
Cache-Control: private, max-age=300 # Solo cache del cliente
Cache-Control: no-cache # Revalidar siempre
Cache-Control: no-store # No cachear
Vary header
Vary: Accept-Language, Accept-Encoding
9. HATEOAS (Hypermedia as the Engine of Application State)
Incluye links a recursos relacionados para hacer la API auto-descriptiva.
{
"id": 123,
"nombre": "Juan",
"email": "juan@example.com",
"_links": {
"self": {
"href": "/usuarios/123"
},
"pedidos": {
"href": "/usuarios/123/pedidos"
},
"direcciones": {
"href": "/usuarios/123/direcciones"
},
"actualizar": {
"href": "/usuarios/123",
"method": "PATCH"
},
"eliminar": {
"href": "/usuarios/123",
"method": "DELETE"
}
}
}
10. Seguridad
HTTPS siempre
❌ http://api.ejemplo.com
✅ https://api.ejemplo.com
Autenticación y autorización
# Bearer Token (JWT)
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# API Key
X-API-Key: sk_live_abc123def456
# OAuth 2.0
Authorization: Bearer ya29.a0AfH6SMBx...
CORS
app.use(cors({
origin: ['https://app.ejemplo.com'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400
}));
Validación de entrada
const { body, validationResult } = require('express-validator');
app.post('/usuarios',
body('email').isEmail().normalizeEmail(),
body('nombre').trim().isLength({ min: 2, max: 50 }),
body('edad').optional().isInt({ min: 18, max: 120 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
}
// Procesar...
}
);
11. API Gateway Pattern
Para arquitecturas de microservicios, usa un API Gateway.
Cliente → API Gateway → [Auth Service]
→ [User Service]
→ [Order Service]
→ [Payment Service]
Responsabilidades del Gateway:
- Enrutamiento
- Autenticación/Autorización
- Rate limiting
- Caching
- Logging y monitoreo
- Transformación de requests/responses
Herramientas:
- Kong
- AWS API Gateway
- Azure API Management
- Google Cloud Endpoints
12. Documentación con OpenAPI/Swagger
openapi: 3.0.0
info:
title: API de Usuarios
version: 1.0.0
description: API para gestión de usuarios
paths:
/usuarios:
get:
summary: Listar usuarios
parameters:
- name: page
in: query
schema:
type: integer
default: 1
responses:
'200':
description: Lista de usuarios
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Usuario'
components:
schemas:
Usuario:
type: object
properties:
id:
type: integer
nombre:
type: string
email:
type: string
format: email
13. Monitoreo y observabilidad
// Logging estructurado
logger.info('Usuario creado', {
userId: user.id,
email: user.email,
requestId: req.id,
duration: Date.now() - startTime
});
// Métricas
metrics.increment('api.usuarios.created');
metrics.timing('api.usuarios.create.duration', duration);
// Tracing distribuido (OpenTelemetry)
const span = tracer.startSpan('create-user');
span.setAttribute('user.email', email);
// ... operación ...
span.end();
Caso de estudio: Stripe API
Stripe es considerada una de las mejores APIs del mundo. Sus principios:
- Consistencia: Mismos patrones en toda la API
- Versionado: Versionado por fecha (2025-09-10)
- Idempotencia: Headers de idempotencia para prevenir duplicados
- Webhooks: Notificaciones de eventos
- Documentación: Ejemplos en múltiples lenguajes
- SDKs: Librerías oficiales en todos los lenguajes populares
Conclusión
Las mejores prácticas para APIs RESTful incluyen:
- Diseño RESTful: Recursos como sustantivos, métodos HTTP correctos
- Versionado: Desde el día 1
- Códigos de estado: Usar códigos HTTP apropiados
- Paginación: Cursor-based para grandes datasets
- Rate limiting: Proteger contra abuso
- Caching: ETags y Cache-Control
- HATEOAS: Links a recursos relacionados
- Seguridad: HTTPS, autenticación, validación
- Documentación: OpenAPI/Swagger
- Monitoreo: Logging, métricas, tracing
Siguiendo estas prácticas empresariales, puedes construir APIs que escalan a millones de usuarios como lo hacen Stripe, Twilio, GitHub y Shopify.
Gustavo Leyva
Desarrollador de Software