GL
Backend 10 de septiembre de 2025 20 min de lectura Por Gustavo Leyva

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.

#API #REST #Backend #Mejores Prácticas #API Gateway
M

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:

  1. Consistencia: Mismos patrones en toda la API
  2. Versionado: Versionado por fecha (2025-09-10)
  3. Idempotencia: Headers de idempotencia para prevenir duplicados
  4. Webhooks: Notificaciones de eventos
  5. Documentación: Ejemplos en múltiples lenguajes
  6. 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.

GL

Gustavo Leyva

Desarrollador de Software

← Volver al blog