APIs RESTful Modernas: Mejores Prácticas y Patrones de Diseño
El desarrollo de APIs RESTful bien diseñadas es fundamental en la arquitectura moderna de aplicaciones. En este artículo exploraremos las mejores prácticas y patrones para crear APIs robustas y escalables.
Principios Fundamentales de REST
1. Recursos como Sustantivos
Las URLs deben representar recursos, no acciones:
✅ Correcto:
GET /api/users/123
POST /api/orders
PUT /api/products/456
❌ Incorrecto:
GET /api/getUser/123
POST /api/createOrder
PUT /api/updateProduct/456
2. Uso Correcto de Métodos HTTP
- GET: Obtener recursos (idempotente)
- POST: Crear nuevos recursos
- PUT: Actualizar recursos completos (idempotente)
- PATCH: Actualizaciones parciales
- DELETE: Eliminar recursos (idempotente)
3. Códigos de Estado Apropiados
// Respuestas exitosas
200 OK // Solicitud exitosa
201 Created // Recurso creado
204 No Content // Eliminación exitosa
// Errores del cliente
400 Bad Request // Datos inválidos
401 Unauthorized // No autenticado
403 Forbidden // No autorizado
404 Not Found // Recurso no existe
// Errores del servidor
500 Internal Error // Error interno
503 Service Unavailable // Servicio no disponible
Estructura de Respuestas Consistente
Respuestas de Éxito
{
"success": true,
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
},
"meta": {
"timestamp": "2024-03-10T10:00:00Z",
"version": "v1"
}
}
Respuestas de Error
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Los datos proporcionados son inválidos",
"details": [
{
"field": "email",
"message": "Formato de email inválido"
}
]
},
"meta": {
"timestamp": "2024-03-10T10:00:00Z",
"requestId": "req-123456"
}
}
Versionado de APIs
1. Versionado por URL
GET /api/v1/users
GET /api/v2/users
2. Versionado por Header
GET /api/users
Accept: application/vnd.api+json;version=1
3. Versionado por Parámetro
GET /api/users?version=1
Recomendación: Usar versionado por URL para simplicidad y claridad.
Autenticación y Seguridad
JWT (JSON Web Tokens)
// Ejemplo de implementación con Node.js
const jwt = require('jsonwebtoken');
// Generar token
const token = jwt.sign(
{ userId: 123, role: 'admin' },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
// Middleware de verificación
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Token requerido' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.status(403).json({ error: 'Token inválido' });
req.user = user;
next();
});
}
Mejores Prácticas de Seguridad
- HTTPS: Siempre usar conexiones seguras
- Rate Limiting: Limitar solicitudes por IP/usuario
- Validación de Entrada: Validar y sanitizar todos los datos
- CORS: Configurar correctamente Cross-Origin Resource Sharing
- Headers de Seguridad: Implementar headers como HSTS, CSP, etc.
Paginación y Filtrado
Paginación
// Parámetros de consulta
GET /api/users?page=1&limit=20&sort=name&order=asc
// Respuesta con metadatos de paginación
{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 250,
"pages": 13,
"hasNext": true,
"hasPrev": false
}
}
Filtrado Avanzado
GET /api/products?category=electronics&minPrice=100&maxPrice=500&inStock=true
Documentación con OpenAPI
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users:
get:
summary: Lista todos los 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/User'
Optimización de Rendimiento
1. Caching
// Redis para caching
const redis = require('redis');
const client = redis.createClient();
app.get('/api/users/:id', async (req, res) => {
const cacheKey = `user:${req.params.id}`;
// Verificar cache
const cached = await client.get(cacheKey);
if (cached) {
return res.json(JSON.parse(cached));
}
// Obtener de base de datos
const user = await User.findById(req.params.id);
// Guardar en cache (5 minutos)
await client.setex(cacheKey, 300, JSON.stringify(user));
res.json(user);
});
2. Compresión
const compression = require('compression');
app.use(compression());
3. ETags para Caching Cliente
app.get('/api/users/:id', (req, res) => {
const user = getUserById(req.params.id);
const etag = generateETag(user);
res.set('ETag', etag);
if (req.headers['if-none-match'] === etag) {
return res.status(304).end();
}
res.json(user);
});
Testing de APIs
Pruebas con Jest y Supertest
describe('Users API', () => {
test('GET /api/users should return users list', async () => {
const response = await request(app)
.get('/api/users')
.expect(200);
expect(response.body.success).toBe(true);
expect(Array.isArray(response.body.data)).toBe(true);
});
test('POST /api/users should create user', async () => {
const userData = {
name: 'John Doe',
email: 'john@example.com'
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
expect(response.body.data.name).toBe(userData.name);
});
});
Monitoreo y Logging
Logging Estructurado
const winston = require('winston');
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'api.log' })
]
});
// Middleware de logging
app.use((req, res, next) => {
logger.info('API Request', {
method: req.method,
url: req.url,
ip: req.ip,
userAgent: req.get('User-Agent')
});
next();
});
Métricas de Rendimiento
const prometheus = require('prom-client');
const httpRequestDuration = new prometheus.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status']
});
// Middleware para métricas
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestDuration
.labels(req.method, req.route?.path || req.url, res.statusCode)
.observe(duration);
});
next();
});
Conclusión
El desarrollo de APIs RESTful de calidad requiere atención a múltiples aspectos: desde el diseño de recursos hasta la implementación de seguridad, rendimiento y monitoreo.
Puntos clave a recordar:
- Seguir los principios REST consistentemente
- Implementar autenticación y autorización robustas
- Documentar thoroughly con OpenAPI
- Implementar caching y optimizaciones de rendimiento
- Establecer monitoring y logging comprehensivos
- Crear tests exhaustivos
En futuros artículos profundizaremos en temas específicos como GraphQL, WebSockets, y arquitecturas de microservicios.