Пагинация
API Пачки использует cursor-based пагинацию для всех методов, возвращающих списки. Это обеспечивает стабильную работу при добавлении и удалении записей.
Две группы методов с пагинацией
В API существует две группы методов, возвращающих разную структуру meta — алгоритмы обхода и условия конца данных у них отличаются. Перед интеграцией убедитесь, что вы знаете, к какой группе относится используемый метод.
- Списочные методы — возвращают
meta.paginateс четырьмя полями:next_page,prev_page,has_next,has_prev. - Методы поиска — возвращают
metaс полемtotalиpaginate.next_page(только курсор «вперёд», безprev_page/has_next/has_prev). К этой группе по поведению также относится GETСписок сотрудников с заданным параметромquery—metaвозвращается в упрощённом формате.
Параметры запроса
| Параметр | Тип | Описание |
|---|---|---|
limit | integer | Количество записей на странице. Допустимые значения зависят от метода: большинство списочных методов — 1–50, GETСписок прочитавших сообщение — до 300, GETПоиск чатов — до 100, GETПоиск сотрудников и GETПоиск сообщений — до 200 |
cursor | string | Курсор для получения страницы (значение meta.paginate.next_page или meta.paginate.prev_page из предыдущего ответа) |
Списочные методы
Списочные методы возвращают объект meta.paginate с четырьмя полями:
| Поле | Тип | Описание |
|---|---|---|
next_page | string | Курсор на следующую страницу (вперёд по сортировке). Используйте для обхода всех записей. |
prev_page | string | Курсор на предыдущую страницу (назад по сортировке). Используйте для polling новых записей «сверху» списка. |
has_next | boolean | Есть ли ещё данные на следующей странице. На последней странице — false. |
has_prev | boolean | Есть ли ещё данные на предыдущей странице. На первом запросе без курсора — false. |
К этой группе относятся: GETСписок сотрудников (без query), GETСписок чатов, GETСписок участников чата, GETСписок сообщений чата, GETСписок реакций, GETСписок прочитавших сообщение, GETСписок тегов сотрудников, GETСписок сотрудников тега, GETСписок напоминаний, GETЖурнал аудита событий, GETИстория событий.
next_page, prev_page, has_next, has_prev всегда присутствуют в ответе — даже когда data пустой. Курсоры никогда не бывают null. Признак конца данных — has_next: false (вперёд) или has_prev: false (назад), а не пустой data. Количество записей в data может быть меньше limit и на промежуточных страницах — не полагайтесь на длину массива. Курсор — непрозрачный токен: не парсите, не конструируйте вручную и не сохраняйте между сессиями. Всегда явно указывайте limit — не полагайтесь на значение по умолчанию.Сценарии и состояния meta.paginate
Поведение полей meta.paginate для типичных сценариев пагинации (на примере GETСписка чатов с DESC-сортировкой):
| Сценарий | Запрос | data | next_page | prev_page | has_next | has_prev |
|---|---|---|---|---|---|---|
| Первый запрос, есть данные | cursor=null, limit=50 | 50 записей | курсор от records.last | курсор от records.first | true | false |
| Первый запрос, БД пуста | cursor=null, limit=50 | 0 записей | пустой курсор (для polling) | пустой курсор (для polling) | false | false |
| Идём вперёд по страницам | cursor=<next_page> | 50 записей | новый forward-курсор | от records.first | true | true |
| Дошли до последней страницы | cursor=<next_page> | менее 50 записей | от records.last | от records.first | false | true |
| Polling старого хвоста (новых нет) | cursor=<next_page последней страницы> | 0 записей | тот же next_page (для следующего polling) | валидный backward-курсор | false | true |
Polling новых через prev_page (новых нет) | cursor=<prev_page> | 0 записей | валидный forward-курсор | тот же prev_page (для следующего polling) | true | false |
Polling новых через prev_page (пришло меньше limit) | cursor=<prev_page> | 3 записи | новый forward-курсор | от records.first | false | false |
Polling новых через prev_page (пришло ≥ limit) | cursor=<prev_page> | 50 записей | новый forward-курсор | от records.first | false | true (продолжайте polling) |
Курсоры next_page и prev_page остаются валидными даже на пустом ответе — можно безопасно повторять запрос с тем же курсором, не теряя точку отсчёта.
Методы поиска
Методы поиска используют отдельный формат meta — без prev_page и без флагов has_next/has_prev. Вместо них возвращается total — общее количество найденных результатов:
| Поле | Тип | Описание |
|---|---|---|
total | integer | Общее количество найденных результатов по запросу. |
paginate.next_page | string | Курсор на следующую страницу. Доступен только курсор «вперёд», без prev_page. |
К этой группе относятся:
- GETПоиск сотрудников
- GETПоиск чатов
- GETПоиск сообщений
- GETСписок сотрудников с заданным параметром
query— возвращает упрощённыйmetaбезprev_page/has_next/has_prev. Если вызывать/usersбезquery, ответ соответствует группе «Списочные методы» с полнымmeta.paginate.
prev_page, has_next, has_prev — поэтому polling новых результатов через prev_page здесь не работает. Конец данных определяйте по совпадению числа уже полученных записей с total или по пустому data в очередном ответе. Курсор next_page — непрозрачный токен, всегда указывайте limit явно.Обход всех записей
Передайте next_page в параметр cursor следующего запроса. Условие остановки зависит от группы метода: для списочных — has_next: false, для методов поиска — равенство числа полученных записей значению total.
Для списочных методов
Для методов поиска
Polling новых данных
Курсор prev_page доступен только в списочных методах и позволяет получать записи, появившиеся «перед» первой страницей, без полного рефетча. Это полезно для отслеживания новых сообщений в чате, новых чатов и других обновлений. В методах поиска prev_page отсутствует — там polling новых результатов через курсор не предусмотрен.
Алгоритм
- Выполните первый запрос без курсора и сохраните
prev_pageиз ответа. - Периодически делайте запрос с
cursor=prev_page. - Если
dataне пуст — появились новые записи. Обновитеprev_pageиз ответа. - Если
has_prevравенtrue— новых записей больше, чемlimit. Продолжайте запрашивать поprev_page, покаhas_prevне станетfalse.
Пока новых записей нет, ответ возвращает пустой data и тот же prev_page — можно безопасно повторять запрос с тем же курсором, не теряя точку отсчёта.
c1 < c2 < ... < c8 при сортировке DESC, первый запрос с prev_page вернёт ближайшие к anchor: [c5, c4, c3, c2, c1] (5 записей в порядке сортировки), а следующий запрос по обновлённому prev_page — оставшиеся [c8, c7, c6]. Внутри batch'а порядок корректный, но между batch'ами клиенту нужно мерджить с учётом сортировки.id. Если у записи на границе курсора обновится поле сортировки (например, last_message_at у первого чата из исходной выдачи), при polling по prev_page она вернётся как «новая» — у неё теперь больший last_message_at, чем зафиксированный в anchor курсора. Это не баг, а корректное поведение: бэкенд сообщает «эта запись изменила позицию», UI должен обновить превью/таймстамп и перерасставить элемент в списке. Всегда дедуплицируйте polling-результаты по id при мердже с уже отрендеренным списком.