Praktický návod
Jak implementovat stránkování API
Vracej omezené a předvídatelné části kolekce. Klient nesmí stáhnout milion řádků jedním requestem.
Nejdřív stručně
Offset, nebo cursor?
Pagination dělí kolekci v API na menší stránky. Parametr limit určuje velikost stránky a offset počet přeskočených záznamů. Je to jednoduché, ale vysoký offset bývá pomalý a při změnách kolekce může položky vynechat nebo zopakovat.
Cursor označuje poslední viděnou pozici ve stabilním řazení. Klient ho pošle v query stringu a server pokračuje za ní. Pro velký nebo průběžně měněný seznam je cursor obvykle bezpečnější.
Připrav si
Co musíš rozhodnout předem
Stránkování je součást kontraktu endpointu. Nejdřív definuj pořadí a teprve potom parametry.
- Kolekční endpoint, například GET /api/products.
- Stabilní a jednoznačné řazení. Samotné created_at nestačí; doplň ho unikátním id.
- Výchozí a maximální limit, například 25 a 100. Hodnotu z requestu vždy validuj a omez.
- Rozhodnutí, zda klient potřebuje přesný celkový počet položek. COUNT nad velkou tabulkou může být drahý.
Kroky 1 až 3
Navrhni stabilní stránkovací kontrakt
Začni jednoduchým tvarem odpovědi a u databázového dotazu zachovej stejné řazení na každé stránce.
1. Definuj parametry a odpověď
- Přijmi limit, nastav výchozí hodnotu a omez ji na rozumné maximum. Neplatné číslo vrať jako chybu 400, nebo zdokumentuj jeho normalizaci.
- Nedovol klientovi libovolný sloupec řazení. Přijmi jen předem povolené hodnoty a směr ASC nebo DESC.
- JSON odpověď drž konzistentní: items obsahuje data a page objekt další cursor, informaci hasMore nebo použitý limit.
- Celkový počet vracej jen tehdy, když ho klient opravdu potřebuje a umíš ho spočítat levně. Cursorové stránkování ho ke své funkci nepotřebuje.
GET /api/products?limit=25 HTTP standard k práci s URI 2. Pro malé stabilní seznamy použij offset
- Pro administraci nebo krátký katalog může být limit a offset nejčitelnější volba. První stránka má offset 0, druhá offset rovný limitu.
- V SQL vždy přidej ORDER BY. Bez něj databáze negarantuje stejné pořadí mezi dvěma requesty.
- Řadíš-li podle neunikátní hodnoty, doplň unikátní tie-breaker: ORDER BY created_at DESC, id DESC.
- Počítej s tím, že vložený nebo smazaný řádek před aktuálním offsetem může posunout následující stránku.
SELECT id, name, created_at
FROM product
ORDER BY created_at DESC, id DESC
LIMIT :limit OFFSET :offset; Oficiální PostgreSQL dokumentace k LIMIT a OFFSET 3. Pro velké nebo živé seznamy použij cursor
- Cursor vytvoř z poslední položky stránky, například z dvojice created_at a id. Klient ho má pouze vrátit; nemusí rozumět jeho obsahu.
- Hodnoty zakóduj do neprůhledného řetězce, po přijetí je bezpečně dekóduj a validuj. Pokud cursor chrání oprávnění nebo filtry, také ho podepiš.
- Dotaz pokračuje striktně za poslední dvojicí. Směr porovnání musí odpovídat směru ORDER BY.
- Načti limit + 1 řádků. Přebytečný řádek řekne, že existuje další stránka; klientovi ho neposílej.
SELECT id, name, created_at
FROM product
WHERE (created_at, id) < (:createdAt, :id)
ORDER BY created_at DESC, id DESC
LIMIT :limitPlusOne; Oficiální PostgreSQL dokumentace k porovnání řádků Krok 4
Otestuj hranice mezi stránkami
Nejdůležitější chyby vznikají při přechodu na další stránku a při souběžné změně dat.
-
Projdi celou kolekci
Začni bez cursoru a pokračuj pomocí nextCursor, dokud hasMore není false. Žádné ID se nesmí opakovat a počet položek na stránce nesmí překročit limit.
curl -s 'http://localhost:8000/api/products?limit=2' -
Vlož záznam mezi dvěma requesty
Po načtení první stránky přidej nový produkt a pokračuj cursorem. Již vrácené položky se nemají objevit znovu.
-
Ověř neplatné vstupy
Vyzkoušej limit 0, záporný offset, limit nad maximem a poškozený cursor. Endpoint má vrátit předvídatelnou chybu, ne databázovou výjimku.
curl -i 'http://localhost:8000/api/products?limit=25&cursor=broken'
Když to zlobí
Nejčastější chyby
Položka se objeví na dvou stránkách
Ověř jednoznačné řazení a zahrň všechny jeho hodnoty do cursoru. created_at bez unikátního id není stabilní hranice.
Dotaz s vysokým offsetem zpomaluje
Databáze musí přeskočené řádky stále najít. Přejdi na cursorové stránkování a přidej index odpovídající filtrům a ORDER BY.
Další stránka je prázdná, i když hasMore bylo true
hasMore odvozuj z načtení limit + 1, ne z toho, že stránka má právě limit položek. Data se také mohla mezi requesty změnit; prázdnou stránku musí klient bezpečně zvládnout.
Cursor lze použít s jiným filtrem
Zahrň do podepsaného cursoru otisk relevantních filtrů a řazení, nebo cursor při jejich změně odmítni. Jinak hranice nepatří ke stejnému seznamu.
Hotovo
API vrací předvídatelné stránky.
API teď chrání databázi limitem a klientovi dává stabilní cestu kolekcí. Offset nech pro malé seznamy; cursor použij tam, kde data rostou nebo se za běhu mění.