Перейти до вмісту
NimbusNexus

Пагінація

Ендпоінти списків (GET /v1/vms, GET /v1/snapshots тощо) розбивають результати на сторінки за непрозорими курсорами, а не за числовими зсувами. Пагінація за курсором — правильний вибір за замовчуванням для списків ресурсів, які змінюються, поки ви гортаєте сторінки: вона повертає кожен елемент рівно один раз, навіть якщо між запитами сторінок рядки додаються чи видаляються.

Формат

Два параметри query на кожному ендпоінті списку:

  • limit — розмір сторінки. За замовчуванням 50, максимум 200. Понад 200 запит не проходить валідацію: 422 validation_error (Помилки).
  • cursor — непрозорий вказівник на наступну сторінку. У першому запиті не передавайте його.

Кожна відповідь зі списком містить:

JSON
{
  "items": [ /* ... */ ],
  "pagination": {
    "next_cursor": "eyJpZCI6InZtXzAxSEc3Li4uIn0=",
    "total_count": null
  }
}

Коли next_cursor дорівнює null, ви дійшли до останньої сторінки.

Обхід сторінок

TypeScript
async function listAllVms(): Promise<VM[]> {
  const all: VM[] = []
  let cursor: string | undefined

  do {
    const url = new URL('https://api.nimbusnexus.net/v1/vms')
    url.searchParams.set('limit', '200')
    if (cursor) url.searchParams.set('cursor', cursor)

    const res = await fetch(url, { headers: auth })
    const { items, pagination } = await res.json()

    all.push(...items)
    cursor = pagination.next_cursor ?? undefined
  } while (cursor)

  return all
}

Три правила:

  1. Курсори непрозорі. Не намагайтеся їх розбирати чи складати самі. Вони кодують стан сортування й фільтрів з вашого початкового запиту, тому курсор від одного запиту не працює в іншому.
  2. Не змінюйте фільтри посеред пагінації. Якщо ви почнете з ?status=running, а для сторінки 2 додасте ?region=us-east-1, поведінка буде невизначеною. Коли фільтри змінюються, починайте спочатку (без курсора).
  3. Зупиняйтеся, коли next_cursor дорівнює null. Не намагайтеся гортати далі «про всяк випадок» — курсор є єдиним достовірним сигналом кінця списку.

Чому курсори, а не ?page=2&per_page=50

Пагінація за зсувом ламається у двох реальних сценаріях:

Вставки під час пагінації. Якщо ви отримали сторінку 1 (елементи 1–50), потім хтось створив нову VM, а тоді ви отримали сторінку 2 (елементи 51–100), то побачите елемент 50 двічі (тепер він на позиції 51) і так і не побачите нового елемента. Курсори фіксують, «де ви були», — наступна сторінка починається після того місця, де ви справді були, а не з абсолютного зсуву.

Видалення під час пагінації. Та сама проблема навпаки: якщо між запитами сторінок VM видаляють, клієнт із пагінацією за зсувом пропускає один елемент. Клієнти з пагінацією за курсором — ні.

Ці граничні випадки важливі за будь-якого нетривіального масштабу. Ми за замовчуванням використовуємо пагінацію за курсором, щоб вони вам не дошкуляли.

total_count

Поле total_count у pagination на більшості ендпоінтів дорівнює null — так задумано. Підрахунок усіх елементів у списку зі сторінками — дорогий запит, який стає дедалі менш корисним зі зростанням N («у вас 14 328 VM в усіх ваших проєктах» — і що з цим робити?). Якщо total_count є, його описано на сторінці довідника цього ендпоінта; якщо його немає, не покладайтеся на нього.

Якщо вам справді потрібна точна кількість, пройдіть усі сторінки до кінця й порахуйте, що отримали.

Порядок сортування

Ендпоінти списків мають сортування за замовчуванням (зазвичай created_at desc, спершу найновіші), описане для кожного ендпоінта окремо. Деякі ендпоінти приймають ?sort=, щоб його змінити; на сторінці довідника перелічено допустимі значення.

Курсор кодує сортування, тому зміна ?sort= між запитами сторінок — один із випадків «невизначеної поведінки»: змінивши сортування, починайте спочатку.

Фільтри

Кожен ендпоінт списку приймає кілька стандартних параметрів фільтрації:

  • created_after / created_before — timestamp-и в RFC 3339
  • updated_after / updated_before — timestamp-и в RFC 3339
  • Фільтри, специфічні для ресурсу (напр. ?region=us-east-1&state=running для VM)

Фільтри комбінуються: відповідь — це AND усіх фільтрів. Які фільтри приймає ресурс, описано на його сторінці довідника.

Коли вам справді потрібні всі елементи

Викликати список із пагінацією в щільному циклі, щоб перебрати всі ресурси, — нормально для разової роботи, але дорого для демонів, які роблять це постійно. Є два кращі патерни:

  1. Вебхуки. Підпишіться на події resource.created / resource.deleted і підтримуйте власний індекс. Див. сторінку Вебхуки платформи.
  2. Ендпоінти масового експорту. Для деяких ресурсів (снапшоти, бекапи, журнали аудиту) є окремий ендпоінт експорту, який віддає весь набір потоком в одній відповіді. Там, де такі ендпоінти є, їх описано на сторінці довідника ресурсу.

Патерн пагінації призначений для інтерактивного використання (таблиця в дашборді, команда list у CLI). Для безперервної синхронізації витрати на пагінацію переважають, і правильна відповідь — вебхуки.

Що далі

  • Угоди — решта формату запиту (коди статусу, помилки, ідемпотентність).
  • Вебхуки платформи — push-альтернатива для безперервної синхронізації.
  • Сторінка довідника будь-якого ендпоінта списку — фільтри й варіанти сортування цього ендпоінта.