Elegir la paginación de una API afecta a lo que ve el consumidor cuando los datos cambian. También condiciona el trabajo que realiza la base de datos para llegar a una página lejana. Por eso la decisión debe partir del uso: navegar por una lista, recorrer todas las filas o producir una exportación reproducible.

Offset indica cuántas filas se saltan antes de devolver el siguiente bloque. Un cursor representa una posición de continuación. Una implementación habitual de cursor utiliza la última clave ordenada recibida para buscar las filas siguientes; no todos los cursores de una API siguen ese mecanismo.

El pedido que aparece dos veces

Considera este ejemplo construido: una API muestra pedidos del más reciente al más antiguo, con dos elementos por página. El orden inicial es E, D, C, B, A.

La primera petición devuelve E y D. Antes de la segunda, llega F. La lista pasa a ser F, E, D, C, B, A. Si la segunda petición utiliza un desplazamiento de dos filas, devuelve D y C. El consumidor ha visto D dos veces.

Una eliminación puede producir el problema contrario. Si desaparece E antes de esa segunda petición, saltar dos filas sobre D, C, B, A devuelve B y A; C queda sin leer.

El problema no implica que offset sea incorrecto para todos los usos. Significa que la posición numérica no identifica un punto estable en una colección que está cambiando.

Continuar desde una clave ordenada

Para recorrer pedidos de reciente a antiguo, podemos ordenar por fecha de creación y, cuando coincida, por identificador. El segundo campo evita que dos pedidos con la misma fecha queden sin una posición inequívoca.

En un ejemplo simplificado de PostgreSQL, la continuación sería:

SELECT id, created_at, status
FROM orders
WHERE tenant_id = :tenant_id
  AND (created_at, id) < (:last_created_at, :last_id)
ORDER BY created_at DESC, id DESC
LIMIT :page_size;

Los parámetros representan valores enlazados por la aplicación; la sintaxis concreta del marcador depende del cliente. Este diseño presupone que los dos campos de orden son no nulos, que el identificador deshace empates y que la fecha de creación no cambia durante el recorrido.

Un índice compatible con el filtro de organización y el orden puede facilitar la búsqueda desde esa posición. Hay que revisar el plan real y la distribución de datos; cambiar offset por una condición de continuación no garantiza por sí solo una consulta eficiente.

PostgreSQL explica que LIMIT necesita un orden predecible y que un OFFSET grande puede resultar ineficiente, porque las filas que se omiten también deben calcularse. Es una razón para medir páginas profundas, además de la primera.

Qué debe contener y proteger el cursor

En lugar de pedir al consumidor que reconstruya la condición SQL, la API puede devolver un token de continuación. Ese token representa la última clave, la versión del formato y los filtros relevantes. Se documenta como opaco: el cliente lo devuelve sin interpretarlo.

Codificar un JSON en Base64 no impide que alguien lo modifique ni oculta su contenido. Si el servidor necesita detectar cambios, puede firmar el token; si almacena el estado en el servidor, puede devolver una referencia aleatoria. En ambos casos debe volver a comprobar la autorización del usuario en cada petición.

La organización autorizada no debe deducirse únicamente de un campo manipulable del cursor. Tampoco se debería aceptar un token generado para otro orden o filtro. Una respuesta de cursor inválido o caducado debe indicar cómo reiniciar el recorrido.

Cursor no equivale a fotografía consistente

La continuación por clave evita algunos desplazamientos provocados por nuevas inserciones. Sin embargo, no congela el conjunto de datos. Una fila puede cambiar de estado y salir del filtro; otra puede entrar después. Si se permite modificar la clave de orden, también puede cruzar el límite ya recorrido.

Cuando una exportación debe representar exactamente un corte, define una estrategia adicional: generar el resultado como trabajo independiente, consultar una versión del conjunto o mantener una instantánea con límites operativos claros.

En PostgreSQL, distintas consultas bajo Read Committed pueden observar cambios confirmados entre ellas; Repeatable Read permite una visión estable dentro de una transacción. La documentación de aislamiento describe estas garantías. Mantener transacciones abiertas a lo largo de muchas peticiones HTTP requiere un diseño específico y no debería introducirse como detalle accidental de la paginación.

Matriz de decisión para el contrato de API

Necesidad del consumidor Enfoque que conviene evaluar Condición que debe explicarse
Saltar directamente a una página concreta Offset La lista puede cambiar entre consultas
Recorrer una colección grande en orden Cursor por clave Orden estable y posibilidad de continuar
Obtener un archivo consistente de un corte Exportación con versión o instantánea Qué incluye el corte y cuánto tiempo se conserva
Mostrar una lista pequeña y poco cambiante Offset puede ser suficiente Medición del coste y orden determinista

No prometas un número total exacto si calcularlo añade un coste que el producto no necesita. Se puede ofrecer una señal de continuación sin acompañarla de un recuento exhaustivo.

Antes de cerrar el contrato, prueba inserciones, borrados, empates de fecha, tokens alterados y cambios de permisos entre páginas. El resultado esperado de cada caso debe quedar descrito en el contrato de la API de datos para quien la integra.

Si estás diseñando una API o backend para un producto de datos, comparte con Nexeus Big Data el patrón de lectura y la consistencia que necesita el consumidor. Esas dos decisiones orientan la paginación mucho más que una preferencia general por un formato de URL.