Principios fundamentales del diseño de APIs REST

Una API REST bien disenada es predecible, consistente y fácil de consumir. No se trata solo de devolver JSON desde un servidor: se trata de crear un contrato claro entre tu backend y los clientes que lo consumen.

Nomenclatura de recursos

Reglas para URLs

Los endpoints deben ser sustantivos en plural que representen recursos, no acciones:

BIEN:
GET    /api/v1/users          -> listar usuarios
GET    /api/v1/users/123      -> obtener un usuario
POST   /api/v1/users          -> crear un usuario
PUT    /api/v1/users/123      -> actualizar un usuario completo
PATCH  /api/v1/users/123      -> actualizar parcialmente
DELETE /api/v1/users/123      -> eliminar un usuario

MAL:
GET    /api/v1/getUsers
POST   /api/v1/createUser
POST   /api/v1/deleteUser/123
GET    /api/v1/user/list

Recursos anidados

Para relaciones, usa anidamiento hasta dos niveles como máximo:

GET /api/v1/users/123/courses           -> cursos del usuario
GET /api/v1/courses/456/lessons         -> lecciones del curso
GET /api/v1/courses/456/lessons/789     -> una leccion específica

# Evitar anidamiento excesivo
MAL:  /api/v1/users/123/courses/456/lessons/789/comments
BIEN: /api/v1/lessons/789/comments

Convenciones de nomenclatura

Regla Ejemplo
Usar kebab-case /learning-paths no /learningPaths
Sustantivos en plural /courses no /course
Minusculas /blog-posts no /Blog-Posts
Sin trailing slash /users no /users/
Sin extensiones /users no /users.json

Métodos HTTP correctos

Método Uso Idempotente Body
GET Leer recurso(s) Si No
POST Crear recurso No Si
PUT Reemplazar recurso completo Si Si
PATCH Actualizar parcialmente No* Si
DELETE Eliminar recurso Si No
HEAD Verificar existencia Si No
OPTIONS Obtener métodos permitidos Si No

*PATCH puede ser idempotente dependiendo de la implementación.

Codigos de estado HTTP

Usar los códigos correctos es fundamental para que los clientes sepan como manejar cada respuesta:

Éxito (2xx)

Código Uso
200 OK Solicitud exitosa con body
201 Created Recurso creado (POST)
204 No Content Éxito sin body (DELETE, PUT)

Redireccion (3xx)

Código Uso
301 Moved Permanently Recurso movido definitivamente
304 Not Modified Cache válido, no enviar body

Error del cliente (4xx)

Código Uso
400 Bad Request JSON mal formado o parametros invalidos
401 Unauthorized No autenticado
403 Forbidden Autenticado pero sin permisos
404 Not Found Recurso no existe
409 Conflict Conflicto (ej: email duplicado)
422 Unprocessable Entity Validación fallida
429 Too Many Requests Rate limit excedido

Error del servidor (5xx)

Código Uso
500 Internal Server Error Error no manejado
502 Bad Gateway Servicio upstream fallo
503 Service Unavailable Servicio temporalmente no disponible

Estructura de respuestas

Respuesta exitosa

{
  "data": {
    "id": "crs_abc123",
    "type": "course",
    "attributes": {
      "title": "HTML desde cero",
      "level": "beginner",
      "lessonsCount": 12
    }
  }
}

Respuesta con lista paginada

{
  "data": [
    { "id": "crs_001", "title": "HTML desde cero" },
    { "id": "crs_002", "title": "CSS Fundamentals" }
  ],
  "pagination": {
    "page": 1,
    "perPage": 20,
    "totalPages": 5,
    "totalItems": 94,
    "hasNextPage": true,
    "hasPrevPage": false
  },
  "links": {
    "self": "/api/v1/courses?page=1",
    "next": "/api/v1/courses?page=2",
    "last": "/api/v1/courses?page=5"
  }
}

Respuesta de error estandarizada

{
  "error": {
    "status": 404,
    "code": "RESOURCE_NOT_FOUND",
    "message": "El curso solicitado no existe.",
    "details": []
  }
}

Paginación

Existen tres estrategias principales:

1. Offset-based (la más simple)

GET /api/v1/courses?page=2&per_page=20

Ventajas: simple de implementar. Desventajas: ineficiente en tablas grandes, resultados inconsistentes si se insertan datos.

2. Cursor-based (la más robusta)

GET /api/v1/courses?cursor=eyJpZCI6MTIzfQ&limit=20

Ventajas: rendimiento consistente, resultados estables. Desventajas: no permite saltar a una página específica.

3. Keyset pagination

GET /api/v1/courses?after_id=crs_123&limit=20

Similar a cursor pero con un campo explícito como clave.

Filtrado, ordenamiento y busqueda

Filtrado

GET /api/v1/courses?level=beginner&category=frontend
GET /api/v1/courses?created_after=2025-01-01
GET /api/v1/courses?tags=html,css

Ordenamiento

GET /api/v1/courses?sort=created_at       # ascendente
GET /api/v1/courses?sort=-created_at      # descendente
GET /api/v1/courses?sort=-rating,title    # multiples campos

Busqueda

GET /api/v1/courses?q=angular+signals
GET /api/v1/search?q=javascript&type=course,blog

Versionamiento

En la URL (recomendado)

GET /api/v1/courses
GET /api/v2/courses

Simple, explícito y fácil de cachear. Es la estrategia más usada por APIs como Stripe, GitHub y Twilio.

En headers (alternativa)

GET /api/courses
Accept: application/vnd.bemorex.v1+json

Más "puro" semanticamente pero más difícil de usar y probar.

Seguridad

Autenticación con Bearer tokens

GET /api/v1/users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Headers de seguridad

X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'

Rate limiting

Incluir headers informativos en cada respuesta:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1706810400
Retry-After: 60

Validación de entrada

  • Validar siempre en el servidor, nunca confiar solo en el cliente
  • Sanitizar inputs para prevenir XSS e inyección SQL
  • Limitar el tamaño del body (ej: 1MB para JSON, 10MB para archivos)
  • Validar Content-Type

Documentacion

Una buena API es una API bien documentada. Usa OpenAPI (Swagger) para generar documentación interactiva:

openapi: 3.1.0
info:
  title: Bemore Learn API
  versión: 1.0.0
paths:
  /api/v1/courses:
    get:
      summary: Listar cursos
      parameters:
        - name: level
          in: query
          schema:
            type: string
            enum: [beginner, intermediate, advanced]
      responses:
        '200':
          description: Lista de cursos

Conclusion

Disenar una buena API REST requiere disciplina y consistencia. Sigue convenciones establecidas, usa códigos HTTP correctamente, estandariza tus respuestas y documenta todo. Una API bien disenada es un multiplicador de productividad para todo tu equipo y tus consumidores. Los patrones presentados aquí son los mismos que usan empresas como Stripe, GitHub y Twilio en sus APIs de clase mundial.