Постраничная выдача

Конверты со смещением и с курсором и почему общее количество иногда отсутствует, а не равно нулю.

Любой эндпоинт-коллекция возвращает конверт, а не голый JSON-массив:

{
  "data": [ ... ],
  "pagination": { ... }
}

Массив всегда лежит под data, а состояние выдачи — всегда под pagination, и никогда плоско в корне ответа. Именно это позволяет одному клиентскому хелперу обходить любую коллекцию API, не зная, с каким эндпоинтом он говорит.

Выдача со смещением — вариант по умолчанию

Большинство коллекций листается смещением. Передавайте limit и offset в query-параметрах:

curl 'https://api.foxguide.io/v1/tables?limit=20&offset=40' \
  -H 'Authorization: Bearer fgd_live_...'
{
  "data": [ ... 20 элементов ... ],
  "pagination": { "total": 137, "limit": 20, "offset": 40, "hasMore": true }
}

Значения по умолчанию — limit: 20, offset: 0. Эндпоинт может ограничивать limit сверху; запрос выше потолка обрезается, а не отклоняется. Останавливайтесь по hasMore: false — это единственное поле, присутствующее в каждом постраничном ответе, и именно его следует брать условием цикла.

Выдача с курсором — коллекции высокой интенсивности

Эндпоинты над часто меняющимися данными (списки сообщений, журналы событий и доставок) листаются курсором. Листание смещением по коллекции, в которую параллельно пишут, пропускает и повторяет строки; курсор — нет.

{
  "data": [ ... ],
  "pagination": {
    "hasMore": true,
    "cursor": "6712f0b3c4a1e2d3f4a5b6c7",
    "direction": "next",
    "limit": 50
  }
}

Передайте полученный cursor обратно в query-параметре cursor, чтобы получить следующую страницу. Два свойства cursor стоит учесть в клиенте:

  • Он может быть явным null, а не просто отсутствовать, когда следующей страницы нет. Обрабатывайте null и отсутствие одинаково, а условием цикла в любом случае берите hasMore.
  • Он непрозрачен. Не разбирайте его, не конструируйте сами и не сохраняйте между изменениями схемы — его единственный контракт в том, что возврат курсора выдаёт следующую страницу.

total необязателен, и его отсутствие — это информация

total присутствует, когда мощность коллекции действительно известна: эндпоинт выполнил настоящий подсчёт по тому же фильтру, что и страница. Он опускается, когда подсчёт невозможен: проксирование во внешний источник, который не сообщает количество, или поверхность только с limit без запроса подсчёта.

Отсутствующий total означает неизвестно. Он никогда не означает ноль и никогда не синтезируется из длины страницы — total, равный data.length, рядом с hasMore: true противоречил бы сам себе на каждой усечённой странице. Пишите клиенты так, чтобы «137 результатов» отображалось только при наличии total, а иначе — «показано 20» или обычные кнопки «дальше/назад».

total ортогонален механизму листания: курсорный эндпоинт тоже может знать свою мощность и сообщать её.

Безопасный обход коллекции

let offset = 0;
const all = [];
for (;;) {
  const res = await fetch(`${base}/v1/tables?limit=100&offset=${offset}`, { headers });
  if (res.status === 429) { await backoff(res); continue; }
  const page = await res.json();
  all.push(...page.data);
  if (!page.pagination.hasMore) break;
  offset += page.pagination.limit;
}

Две вещи, которые этот цикл делает правильно, а наивный — нет: он завершается по hasMore, а не по ожидаемому total, и трактует 429 как повтор, а не как конец коллекции.

← Вся документация