Интеграция выгрузила сделки из Битрикс24 без единой ошибки, но в файле ровно 50 строк. Это не «маленькая база». Так выглядит код, который выполнил один списочный запрос и не прочитал result.next.
Первое, что я бы проверил, — сырой ответ первой страницы. Если в нём есть next, работа не закончена. Передайте это значение в start следующего запроса. Не вычисляйте курсор на глаз.
Что вернуть в контрольном запросе
Для диагностики не запрашивайте карточку сделки целиком. Достаточно ID, TITLE и даты изменения. Чем шире select, тем тяжелее ответ и тем сложнее увидеть ошибку навигации.
| Поле ответа | Что проверяем | Ошибка |
|---|---|---|
result | Массив текущей страницы | Код ожидает объект другой формы |
next | Значение следующего start | Игнорируется после первой страницы |
total | Контрольный общий объём, когда считается | Считается обязательным при start=-1 |
error | Ошибка метода | Пустой result трактуется как конец |
Рабочий цикл по result.next
async function loadAllDeals(webhookUrl) {
const rows = [];
const seenStarts = new Set();
let start = 0;
while (start !== null) {
if (seenStarts.has(start)) throw new Error('Повтор курсора: ' + start);
seenStarts.add(start);
const response = await fetch(webhookUrl + '/crm.deal.list.json', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
order: { ID: 'ASC' },
select: ['ID', 'TITLE', 'DATE_MODIFY'],
start,
}),
});
const body = await response.json();
if (!response.ok || body.error) throw new Error(body.error_description || body.error || response.statusText);
rows.push(...body.result);
start = body.next ?? null;
}
const unique = new Set(rows.map(row => String(row.ID)));
if (unique.size !== rows.length) throw new Error('В выгрузке есть повторные ID');
return rows;
}
Webhook URL берите из секретного хранилища. Не вставляйте его в исходный код и не печатайте в лог. В лог достаточно писать номер страницы, start, количество строк и последний ID.
Пошаговая проверка перед production
- Создайте фильтр, который возвращает больше 50 тестовых сделок, но не всю базу.
- Запустите цикл и сохраните последовательность значений start.
- Проверьте, что каждый следующий start взят из ответа, а не рассчитан как
+50. - Сравните число строк и уникальных ID.
- Повторите выгрузку. При неизменной базе набор ID должен совпасть.
Большая база: не заставляйте REST считать total
Для действительно большой выборки обычная навигация с подсчётом total может стать дорогой. Официальная схема: сортировать по стабильному числовому ID, передавать фильтр >ID последнего элемента и start: -1. Цикл заканчивается, когда получено меньше размера страницы.
У этой схемы есть условие: конкретный метод должен поддерживать фильтр и сортировку по ID. Если во время выгрузки записи удаляются или права меняются, состав выборки тоже меняется. Зафиксируйте окно выгрузки или критерий даты.
PASS и FAIL
- PASS: next исчез только на последней странице; start не повторяется; все ID уникальны.
- PASS: повторная контрольная выгрузка даёт тот же набор при неизменных данных.
- FAIL: файл всегда содержит 50 строк при наличии next.
- FAIL: цикл крутится на одном start или молча завершает работу после error.
Четыре ошибки, которые встречаются чаще всего
Курсор вычисляют вручную. Код делает start += 50, хотя правильное продолжение уже пришло в next.
Пустой массив принимают за успех. Сначала проверьте HTTP и поле error, потом решайте, что список закончился.
batch считают одной успешной операцией. У пакетного метода отдельный подзапрос может попасть в result_error при общем HTTP 200.
Выгружают всё. Слишком широкий select и отсутствие фильтра расходуют лимиты и усложняют восстановление.
Как продолжить выгрузку после сетевого сбоя
Хранить все строки только в памяти удобно для примера, но ненадёжно для ночного обмена. После каждой подтверждённой страницы сохраняйте checkpoint: фильтр, последний успешный start или ID, количество строк и время. Запись checkpoint и данных страницы должна быть согласованной. Иначе после падения приложение запомнит курсор, но не сохранит сами сделки.
Для стандартной пагинации повтор одной страницы безопасен только при дедупликации по ID. Для выборки >ID checkpoint — последний полностью сохранённый ID. Не обновляйте его по первому элементу страницы.
Контроль перед повторным запуском
- Последняя страница данных действительно записана.
- Checkpoint относится к той же версии фильтра и select.
- Webhook не попал в лог или файл состояния.
- Повторная страница обновляет запись по ID, а не создаёт копию.
- После завершения сверяется число уникальных ID.
Если выгрузка идёт параллельно с изменением сделок, зафиксируйте верхнюю границу периода, например дату изменения на момент старта. Без неё поток новых сделок может двигать конец списка быстрее, чем интеграция его догоняет.
Что делать, если не помогло
Остановите ночную выгрузку, сохраните параметры и обезличенный ответ одной страницы. Затем проверьте права пользователя webhook: статья про REST 403 отделяет ошибку доступа от пагинации. Выбор между приложением и webhook разобран отдельно. Если обмен нужен для рабочего процесса, мы можем доработать Битрикс24 с журналом курсоров, восстановлением и проверкой полноты.



