Paginación y rangos de tiempo

Cómo recorrer las entregas y los registros de una colección con un cursor opaco, y por qué una toma un rango de tiempo y los otros no.

Dos endpoints paginan, y lo hacen de forma distinta a propósito:

ListadoRangoOrdenEl cursor está ligado a
Entregasfrom y to, obligatorios, 366 días como máximoDe más reciente a más antiguaEl rango que pidió
Registros de una colecciónninguno: recorre la colección enteraDel cambio más antiguo al más recienteRestaurante, colección y limit

Ambos responden { "data": [...], "nextCursor": string | null }, ambos topan limit en 100 con 50 por defecto, y ambos quieren que se les devuelva el cursor intacto. El resto de esta página es el listado de entregas; la página de colecciones cubre el otro.

Las entregas son inmutables e interesan por fecha, así que toman un rango. Los registros de una colección cambian sobre el sitio, así que un rango sobre ellos sería una mentira: usted recorre la colección y concilia.

El listado de entregas

Toma un rango de tiempo explícito y obligatorio y devuelve una página más un cursor opaco.

GET /v1/restaurants/{restaurantId}/deliveries
  ?from=1787846400000
  &to=1788451200000
  &limit=50
  &cursor=eyJpZCI6…
ParámetroObligatorioReglas
fromMilisegundos desde la época Unix, como cadena de dígitos. Inclusivo.
toMilisegundos desde la época Unix, como cadena de dígitos. Inclusivo.
limitnoDe 1 a 100. Por defecto, 50.
cursornoEl nextCursor de la página anterior, tal cual.

to debe ser mayor o igual que from, y el intervalo debe ser de 366 días como máximo. Cualquier otra cosa responde 400. El rango es obligatorio en lugar de tener un valor por defecto porque un valor por defecto es justo la manera en que una integración empieza en silencio a releer una década de registros cada noche.

Orden

De más reciente a más antigua: descendente por occurredAt, y después descendente por id para deshacer un empate. Dos entregas registradas en el mismo milisegundo tienen así un orden estable y repetible, que es lo que hace que el cursor sea sólido.

Recorrer las páginas

nextCursor es null en la última página y una cadena en las demás. Devuélvalo intacto: es una posición codificada en base64url, no un documento que analizar, y sus interioridades cambiarán sin previo aviso.

async function* deliveries({ apiKey, restaurantId, from, to }) {
  const base = `https://api.backresto.com/v1/restaurants/${restaurantId}/deliveries`;
  let cursor = null;

  do {
    const url = new URL(base);
    url.searchParams.set('from', from);
    url.searchParams.set('to', to);
    url.searchParams.set('limit', '100');
    if (cursor !== null) {
      url.searchParams.set('cursor', cursor);
    }

    const response = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` }
    });
    if (!response.ok) {
      throw new Error(`BackResto ${response.status}`);
    }

    const page = await response.json();
    yield* page.data;
    cursor = page.nextCursor;
  } while (cursor !== null);
}

Mantenga from y to idénticos en todas las páginas de un recorrido. El cursor codifica una posición dentro del rango que pidió; cambiar el rango a mitad del recorrido no es una petición admitida y no le dará lo que espera.

Consultar de forma incremental

Las entregas son inmutables una vez registradas, así que una consulta incremental es sencilla: guarde el occurredAt del registro más reciente que tenga almacenado y úselo como el siguiente from.

Dos detalles que conviene incorporar:

  • Solape a propósito. Empiece la siguiente ventana un poco antes del último registro que vio —una hora sobra— y deduplique por id. Un dispositivo que estuvo sin conexión durante el servicio sube los datos al reconectarse, así que una entrega puede hacerse visible después de una consulta que ya cubría su marca de tiempo.
  • Nunca amplíe más allá de 366 días. Si su tarea lleva mucho tiempo sin ejecutarse, recorra el hueco en ventanas de un año en lugar de pedirlo todo de golpe.

Una página vacía no es prueba de que no ocurriera nada; consulte la nota sobre exhaustividad.

Última actualización: 2026-09-19.