Paginación, Ordenación, Filtrado y Selección de Campos
🎯 Objetivo
Regla
10.1: Se deben implementar mecanismos estandarizados y consistentes en toda la API para manejar colecciones de recursos grandes, permitiendo a los clientes solicitar subconjuntos específicos de datos. 10.2: Esto incluye paginación, ordenación, filtrado y selección de campos, los cuales son obligatorios para endpoints que devuelven colecciones potencialmente grandes.
📖 Concepto y Definición
Regla
10.3: Paginación: Técnica obligatoria para dividir una colección grande de resultados en "páginas" más pequeñas y manejables. 10.4: Ordenación: Permite al cliente especificar el orden en que se devuelven los elementos de una colección. 10.5: Filtrado: Permite al cliente restringir los elementos devueltos de una colección basándose en criterios específicos. 10.6: Selección de Campos (Proyección): Permite al cliente especificar qué campos de cada recurso desea recibir en la respuesta, reduciendo el tamaño de la misma (evitando over-fetching).
Estrategias Obligatorias:
Regla
10.7: Paginación:
- Se debe usar paginación basada en página/tamaño o cursor. La elección debe ser consistente para un mismo tipo de recurso.
- Paginación basada en página/tamaño:
- Usar cuando: La colección es estática o cambia poco frecuentemente
- Usar cuando: Se necesita mostrar el total de páginas/items
- Usar cuando: La navegación aleatoria es un requisito
- Parámetros obligatorios:
page,perPage - Ejemplo:
GET /items?page=2&perPage=10
- Paginación basada en cursor:
- Usar cuando: La colección cambia frecuentemente
- Usar cuando: El rendimiento es crítico con grandes conjuntos de datos
- Usar cuando: No se necesita el total de items/páginas
- Parámetros obligatorios:
perPage,after(onextCursor) - Ejemplo:
GET /items?perPage=10&after=cursor_opaque_123
- Paginación basada en página/tamaño:
- Nomenclatura Estándar para Paginación:
- Los parámetros de paginación deben usar:
pagepara el número de página actualperPagepara el número de elementos por páginatotalPagespara el total de páginastotalItemspara el total de elementos
- Esta nomenclatura es obligatoria y debe ser consistente en toda la API
- Los parámetros de paginación deben usar:
- Independientemente del método, la respuesta debe incluir metadatos de paginación claros y consistentes (ver Norma 5.21), preferiblemente con enlaces HATEOAS (
next,prev, etc.) como se norma en la Sección 6. - Se debe establecer un tamaño de página (
perPage) predeterminado y un máximo permitido para evitar cargas excesivas.
10.8: Ordenación:
- Se debe usar un parámetro de consulta estándar, preferiblemente
sort, para especificar el criterio de ordenación. - El valor debe ser una lista de campos separados por comas.
- Se debe permitir indicar el orden ascendente (predeterminado) o descendente por campo, usando un prefijo (ej.
-para descendente:sort=-createdAt,name). - La API debe documentar claramente qué campos son ordenables.
- Ejemplo:
GET /users?sort=-dateJoined,lastName
10.9: Filtrado:
- Se deben usar parámetros de consulta específicos por campo para el filtrado.
- Los nombres de los parámetros deben coincidir preferiblemente con los nombres de los campos filtrables (
camelCase, Norma 5.6). - Se deben soportar múltiples filtros simultáneamente.
- Para filtros más complejos (ej.
mayor que,menor que,contiene), se pueden usar corchetes ([]) o calificadores estándar (ej.status[ne]=active,price[gte]=100). Esta convención debe ser consistente y documentada. - La API debe documentar claramente qué campos son filtrables y qué operaciones se soportan por campo.
- Ejemplo:
GET /orders?status=completed&createdAt[gte]=2023-01-01
10.10: Selección de Campos (Proyección):
- Se debe usar un parámetro de consulta estándar, preferiblemente
fields, para especificar los campos deseados. - El valor debe ser una lista de nombres de campos separados por comas.
- Si el parámetro
fieldsestá presente, la respuesta solo debe incluir esos campos (y posiblemente identificadores clave o enlacesself). - Si el parámetro
fieldsno está presente, la API debe devolver el conjunto de campos predeterminado (usualmente todos o un subconjunto común). - Ejemplo:
GET /users?fields=userId,username,email
🤔 ¿Por qué es Importante? / Beneficios Clave (Contexto)
- Rendimiento: Reduce la carga en servidor y cliente, y el tráfico de red.
- Escalabilidad: Maneja colecciones grandes eficientemente.
- Usabilidad: Permite a los clientes obtener exactamente lo que necesitan.
- Flexibilidad: Adapta la respuesta a diferentes casos de uso.
✅ Buenas Prácticas y Recomendaciones Clave
Regla
10.11: Se refuerzan las reglas 10.7 a 10.10: El uso de los parámetros (page, perPage, sort, fields, filtros por campo) y convenciones descritas es obligatorio y debe ser consistente en toda la API para endpoints que devuelven colecciones.
Nota sobre Migración: Para APIs existentes que usen offset/limit, se debe planificar una migración gradual a page/perPage. Durante la transición, se pueden soportar ambos formatos, pero se debe documentar claramente que page/perPage es el estándar preferido y que offset/limit será deprecado en futuras versiones.
10.12: Los valores predeterminados y máximos para perPage (paginación) deben ser razonables y estar documentados.
10.13: La documentación (OAS) debe detallar claramente todos los parámetros de paginación, ordenación, filtrado y selección de campos disponibles para cada endpoint de colección, incluyendo los campos aplicables y las operaciones permitidas.
10.14: Para paginación, se recomienda incluir enlaces HATEOAS (first, last, prev, next) en la respuesta (ver Norma 6.9) además de los metadatos numéricos (totalItems, totalPages, etc.).
10.15: Las operaciones de filtrado y ordenación deben ser eficientes en el backend (ej. usando índices de base de datos).
Don'ts (Prohibido):
- 10.16: No se debe devolver una colección completa sin paginación si puede crecer indefinidamente.
- 10.17: No se deben usar nombres de parámetros inconsistentes para estas funcionalidades entre diferentes endpoints.
- 10.18: No se debe permitir ordenar o filtrar por campos no indexados si causa problemas de rendimiento.
- 10.19: No se debe implementar paginación basada en offset/límite en conjuntos de datos que cambian muy frecuentemente si puede llevar a resultados duplicados o saltados entre páginas. En esos casos, se debe usar paginación basada en cursor.
💡 Ejemplos Prácticos (Ilustrativos)
Ejemplo Combinado (Norma 10.7, 10.8, 10.9, 10.10)
Solicitud para obtener la segunda página de 5 usuarios activos (isActive=true), ordenados por fecha de registro descendente (-dateJoined), mostrando solo su ID, nombre de usuario y email:
GET /v1/users?isActive=true&sort=-dateJoined&page=2&perPage=5&fields=userId,username,email HTTP/1.1
Host: api.example.com
Accept: application/json
Respuesta esperada (asumiendo paginación basada en página y envoltura de datos):
{
"data": [
{ "userId": "uuid-6", "username": "user6", "email": "user6@example.com" },
{ "userId": "uuid-7", "username": "user7", "email": "user7@example.com" },
{ "userId": "uuid-8", "username": "user8", "email": "user8@example.com" },
{ "userId": "uuid-9", "username": "user9", "email": "user9@example.com" },
{ "userId": "uuid-10", "username": "user10", "email": "user10@example.com" }
],
"_links": [
{
"rel": "self",
"href": "https://api.example.com/v1/users?isActive=true&sort=-dateJoined&page=2&perPage=5&fields=userId,username,email",
"method": "GET"
},
{
"rel": "first",
"href": "https://api.example.com/v1/users?isActive=true&sort=-dateJoined&page=1&perPage=5&fields=userId,username,email",
"method": "GET"
},
{
"rel": "prev",
"href": "https://api.example.com/v1/users?isActive=true&sort=-dateJoined&page=1&perPage=5&fields=userId,username,email",
"method": "GET"
},
{
"rel": "next",
"href": "https://api.example.com/v1/users?isActive=true&sort=-dateJoined&page=3&perPage=5&fields=userId,username,email",
"method": "GET"
},
{
"rel": "last",
"href": "https://api.example.com/v1/users?isActive=true&sort=-dateJoined&page=30&perPage=5&fields=userId,username,email",
"method": "GET"
}
],
"_meta": {
"timestamp": "2023-10-27T12:00:00Z",
"version": "1.0.0",
"pagination": {
"page": 2,
"perPage": 5,
"totalPages": 30,
"totalItems": 150
}
}
}
🛠️ Herramientas y Consideraciones Adicionales (Contexto)
- Bases de Datos: La implementación eficiente depende en gran medida de las capacidades de consulta y los índices de la base de datos subyacente.
- Frameworks: Algunos frameworks ofrecen soporte integrado o bibliotecas para implementar estas características de forma más sencilla.
- Complejidad de Filtros: Para necesidades de filtrado muy avanzadas, se podrían considerar lenguajes de consulta más expresivos (ej. OData, GraphQL), pero esto debe ser una decisión arquitectónica mayor y consistente, y no debe reemplazar las convenciones básicas aquí normadas para la mayoría de los casos.