Версионирование, пагинация и ответы N+1 — Примеры

Пример 1 — Размер страницы, который обеспечивает сервер

Коллекция заказов возвращает всё, что есть у аккаунта. Клиент начал просить очень большую страницу. Дайте коллекции максимум, который обеспечивает сервер, упорядочение и способ дойти до следующей страницы, — и покажите запрос и ответ так, как их увидит клиент.

GET /orders?limit=5000

200 OK
[ ... 41 208 заказов в одном теле ... ]        (иллюстративно)

Решение

GET /orders?limit=5000&order=placed_at,desc
(сервер обрезает limit до 100)

200 OK
{
  "items": [ ... 100 заказов, сначала новые ... ],
  "next": "/orders?limit=100&cursor=page-3"
}

Максимум принадлежит серверу, поэтому запрос на 5000 обрезается, а не принимается на веру, и ни один вызов не может исчерпать память. Упорядочение указано потому, что курсор осмыслен только относительно него, а переход по next дважды возвращает каждый заказ ровно один раз, даже пока прибывают новые заказы, — чего пагинация со смещением на движущейся коллекции не обещает. Однако обычно ошибаются иначе: limit проверяют отклонением всего запроса, что ломает клиента, который лишь немного переоценил, или оставляют без ограничения вовсе.

Пример 2 — Два запроса вместо пятидесяти одного

В списке заказов теперь показывается имя каждого клиента, и эндпоинт выдаёт один запрос на страницу и ещё по одному на заказ; журнал ниже — со страницы в пятьдесят, а сделать число запросов нужно независимым от числа возвращаемых строк.

GET /orders?page=1                       (50 заказов, иллюстративно)
select * from orders order by placed_at desc limit 50;
select * from customers where id = 7;   ... 50 раз, по одному на заказ
число запросов: 51

Решение

select * from orders order by placed_at desc limit 50;
select * from customers where id in (7, 9, 12, ...);   один запрос на страницу
число запросов: 2

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