Saltar al contenido principal

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 (o nextCursor)
      • Ejemplo: GET /items?perPage=10&after=cursor_opaque_123
  • Nomenclatura Estándar para Paginación:
    • Los parámetros de paginación deben usar:
      • page para el número de página actual
      • perPage para el número de elementos por página
      • totalPages para el total de páginas
      • totalItems para el total de elementos
    • Esta nomenclatura es obligatoria y debe ser consistente en toda la API
  • 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 fields está presente, la respuesta solo debe incluir esos campos (y posiblemente identificadores clave o enlaces self).
  • Si el parámetro fields no 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.