Skip to content

Курсорная пагинация

Задача на постраничную загрузку событий в ленте, где новые элементы могут постоянно появляться в начале списка.

Задача

Сделать пагинацию событий, которая:

  • стабильно работает при изменяющейся ленте;
  • не дает дублей и пропусков между страницами;
  • подходит для бесконечной прокрутки.

Контекст

Обычная offset-пагинация плохо подходит для динамической ленты. Пока пользователь догружает следующую страницу, в начало списка могут прийти новые события, из-за чего смещаются позиции уже существующих записей.

В таком сценарии offset легко приводит к дублям, пропускам и нестабильному порядку результатов.

Ключевое решение

Для API использовал курсорный подход с композитным курсором.

Сервер возвращает:

  • limit;
  • has_more;
  • next_cursor.

next_cursor содержит указатель на последний загруженный элемент в виде пары <timestamp, uuid>, закодированной в base64. Следующий запрос получает только события, которые идут строго после этой пары значений.

Такой подход позволяет избежать дублей и пропусков при изменении ленты и сохраняет стабильный порядок даже при одинаковых timestamp.

Что сделал на backend

  • добавил поддержку курсорной пагинации в /api/events и /api/events/preview с сохранением обратной совместимости;
  • учёл сортировку по timestamp, который хранится строкой, и использовал приведение типа в SQL;
  • добавил endpoint /api/events/type-counts для получения количества событий по типам;
  • подготовил документацию и тесты для новых API.

Что сделал на frontend

  • организовал параллельную загрузку счётчиков и первых страниц событий по типам;
  • реализовал догрузку через IntersectionObserver с rootMargin=200px;
  • добавил передачу next_cursor при загрузке следующих страниц;
  • обработал пограничные сценарии, когда скролл не появляется сразу, observer не срабатывает или позиция прокрутки мешает автоматической догрузке;
  • реализовал плавный infinite scroll для динамической ленты, где новые события могут появляться сверху, а старые подгружаются по мере прокрутки вниз.

Результат

В результате удалось убрать прежнее ограничение на 100 событий и перейти к динамической загрузке полного списка событий из базы по мере прокрутки.

См. также

Сайт обновлен и проверен: