Diseñar APIs REST: mejores prácticas en 2026


Publicado originalmente en Bemorex Learn, nuestra plataforma de aprendizaje. · Read in English
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.
Ejemplos de código
JSON Response
{
"data": {
"id": "usr_abc123",
"type": "user",
"attributes": {
"name": "David Morales",
"email": "[email protected]",
"role": "admin",
"createdAt": "2025-06-15T10:30:00Z",
"updatedAt": "2026-01-20T14:22:00Z"
},
"relationships": {
"organization": {
"id": "org_xyz789",
"name": "Bemorex"
}
}
},
"meta": {
"requestId": "req_f47ac10b",
"timestamp": "2026-01-20T14:22:00Z"
}
}Error Response
{
"error": {
"status": 422,
"code": "VALIDATION_ERROR",
"message": "Los datos enviados no son validos.",
"details": [
{
"field": "email",
"message": "El formato del email no es válido.",
"value": "david@"
},
{
"field": "password",
"message": "La contrasena debe tener al menos 8 caracteres.",
"value": null
}
]
},
"meta": {
"requestId": "req_e89b12d3",
"timestamp": "2026-01-20T14:22:00Z",
"documentation": "https://api.example.com/docs/errors#VALIDATION_ERROR"
}
}