# Pagination

> How to walk any collection with an opaque cursor, and how to keep a copy of it up to date.

Source: https://developers.backresto.com/en/docs/pagination

Every list in this API is read the same way: you walk a
[collection](https://developers.backresto.com/en/docs/collections) page by page, deliveries included.

```http
GET /v1/restaurants/{restaurantId}/collections/{collection}/records
  ?limit=100
  &cursor=eyJpZCI6…
```

| Parameter | Required | Rules |
| --- | --- | --- |
| `limit` | no | 1 to 100. Defaults to 50. |
| `cursor` | no | The `nextCursor` of the previous page, verbatim. |

The answer is `{ "data": [...], "nextCursor": string | null }`.

## No time range

There is no `from` and no `to`. A record changes in place (a correction, a
tombstone), so you walk the collection and reconcile rather than asking for a
window of it. To read one week of deliveries, walk `deliveries` and keep the
records whose `data.timestamp` falls inside the week.

## Ordering

Oldest change first: ascending by `sequence`, the position of each record's
latest change in the stream. Compare two `sequence` values; do not do
arithmetic on one.

## Walking the pages

`nextCursor` is `null` on the last page and a string otherwise. Pass it back
untouched: it is an opaque position, not a document to parse, and its
internals will change without notice.

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

  do {
    const url = new URL(base);
    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);
}
```

**A walk is a consistent snapshot.** The first page pins the position of the
stream, and every later page is answered as of that same position. Records
written while you are paging do not shift the pages under you. You see them on
your next walk.

The cursor is bound to the restaurant, the collection **and the limit**. Change
the page size mid-walk and it answers `400`: pick a limit, keep it for the
whole walk.

## Keeping a copy up to date

There is no `since` parameter today. A refresh is another walk, and you
reconcile with what you already hold:

- **Key on `id`** within a collection, and overwrite when the `sequence` you
  receive is higher than the one you stored.
- **Honour tombstones.** `deleted: true` is the delete instruction; applying it
  is the only way your copy stops diverging.
- **Expect late arrivals.** A device that was offline during service uploads
  when it reconnects, so a record can appear after a walk that would have
  covered its `data.timestamp`. Report on `capturedAt`, sync on `receivedAt`.

An empty page is not proof that nothing happened. See
[the note on completeness](https://developers.backresto.com/en/docs/support).
