Paginação e intervalos de tempo

Como percorrer as entregas e os registos de uma coleção com um cursor opaco, e porque é que uma recebe um intervalo de tempo e a outra não.

Dois endpoints paginam, e fazem-no de maneira diferente de propósito:

ListaIntervaloOrdenaçãoO cursor está ligado a
Entregasfrom e to, obrigatórios, no máximo 366 diasDo mais recente para o mais antigoO intervalo que pediu
Registos de uma coleçãonenhum — percorre a coleção inteiraDa alteração mais antiga para a mais recenteRestaurante, coleção e limit

Ambas respondem { "data": [...], "nextCursor": string | null }, ambas limitam o limit a 100 com 50 por omissão, e ambas querem o cursor devolvido intacto. O resto desta página é sobre a lista de entregas; a página das coleções trata da outra.

As entregas são imutáveis e interessam por data, por isso recebem um intervalo. Os registos de uma coleção mudam no sítio, por isso um intervalo sobre eles seria uma mentira — percorre-se a coleção e reconcilia-se.

A lista de entregas

Recebe um intervalo de tempo explícito e obrigatório e devolve uma página mais um cursor opaco.

GET /v1/restaurants/{restaurantId}/deliveries
  ?from=1787846400000
  &to=1788451200000
  &limit=50
  &cursor=eyJpZCI6…
ParâmetroObrigatórioRegras
fromsimMilissegundos desde a época Unix, como uma cadeia de dígitos. Inclusivo.
tosimMilissegundos desde a época Unix, como uma cadeia de dígitos. Inclusivo.
limitnãoDe 1 a 100. Por omissão, 50.
cursornãoO nextCursor da página anterior, tal e qual.

to tem de ser maior ou igual a from, e o intervalo tem de ser de, no máximo, 366 dias. Qualquer outra coisa responde 400. O intervalo é obrigatório em vez de assumido por omissão porque um intervalo assumido é a forma como uma integração começa, sem ninguém dar por isso, a reler uma década de registos todas as noites.

Ordenação

Do mais recente para o mais antigo: por occurredAt descendente, depois por id descendente para desempatar. Duas entregas registadas no mesmo milissegundo têm, por isso, uma ordem estável e repetível, que é o que torna o cursor sólido.

Percorrer as páginas

nextCursor é null na última página e uma cadeia de caracteres nas restantes. Devolva-o intacto — é uma posição codificada em base64url, não um documento para analisar, e o seu conteúdo vai mudar sem 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);
}

Mantenha from e to idênticos em todas as páginas de um percurso. O cursor codifica uma posição dentro do intervalo que pediu; mudar o intervalo a meio do percurso não é um pedido suportado e não lhe vai dar o que espera.

Consultar de forma incremental

As entregas são imutáveis assim que registadas, por isso uma consulta incremental é simples: guarde o occurredAt do registo mais recente que tem armazenado e use-o como o from seguinte.

Dois pormenores que vale a pena implementar:

  • Sobreponha de propósito. Comece a janela seguinte um pouco antes do último registo que viu — uma hora chega — e elimine duplicados pelo id. Um dispositivo que esteve offline durante o serviço envia os dados quando volta a ligar-se, por isso uma entrega pode tornar-se visível depois de uma consulta que já cobria a sua data e hora.
  • Nunca alargue para além de 366 dias. Se o seu processo não corre há muito tempo, percorra o intervalo em janelas de um ano em vez de o pedir todo de uma vez.

Uma página vazia não é prova de que nada aconteceu — veja a nota sobre a completude dos dados.

Última atualização em 2026-09-19.