Pagination
Jeder Listen-Endpoint (GET /api/v1/<resource>) liefert eine Seite, nie den
kompletten Datenbestand. Die Seite steht in data, die Blätter-Information in
meta.
{
"data": [ … ],
"meta": {
"nextCursor": "0192f2c0-8d1c-7a3b-9e4f-d3e7f1a2b5c8",
"hasMore": true,
"limit": 50
}
}
Es gibt zwei Blätter-Verfahren: Offset (der Standardweg) und Cursor (für große Durchläufe). Beide steuern Sie über Query-Parameter.
Query-Parameter
| Parameter | Typ | Default | Bedeutung |
|---|---|---|---|
limit |
number | 50 |
Zeilen pro Seite. Hart auf 200 begrenzt (siehe Hinweis unten). |
offset |
number | 0 |
Zeilen überspringen. Offset-Paging. |
cursor |
string | – | UUID der zuletzt gelesenen Zeile. Cursor-Paging. |
sort |
string | – | Feld, nach dem sortiert wird. |
order |
string | asc |
asc oder desc. Nur wirksam zusammen mit sort. |
withTotal |
boolean | false |
Wenn true, enthält meta zusätzlich total (exakte Trefferzahl). |
Offset-Paging
Der Standardweg. Sie erhöhen offset um limit, bis hasMore auf false
steht.
curl "https://os.codemeta.de/api/v1/tickets?limit=100&offset=200" \
-H "X-Tenant-Id: $TENANT" \
-b cookies.txt
Offset-Paging funktioniert mit jeder Sortierung und ist die richtige Wahl für
UI-Seiten, Sprünge zu einer bestimmten Seite und Durchläufe bis einige tausend
Zeilen. Für sehr tiefe Durchläufe wird offset mit wachsender Tiefe langsamer
— dort ist Cursor-Paging die bessere Wahl.
Cursor-Paging
Statt zu überspringen, setzen Sie beim zuletzt gelesenen Datensatz an. Der
cursor ist die nextCursor-Angabe der vorherigen Antwort.
CURSOR="0192f2c0-8d1c-7a3b-9e4f-d3e7f1a2b5c8" # meta.nextCursor der Vorseite
curl "https://os.codemeta.de/api/v1/tickets?limit=100&sort=_id&order=asc&cursor=$CURSOR" \
-H "X-Tenant-Id: $TENANT" \
-b cookies.txt
Ein ungültiger Cursor (kein UUID-Format) wird mit 400 abgelehnt, nicht still ignoriert.
Das meta-Objekt
| Feld | Typ | Immer da? | Bedeutung |
|---|---|---|---|
nextCursor |
string | null | ja | ID der letzten Zeile dieser Seite, oder null bei der letzten Seite. |
hasMore |
boolean | ja | Ob mindestens eine weitere Zeile existiert. |
limit |
number | ja | Das tatsächlich angewandte Limit — nach der 200er-Kappung. |
total |
number | nein | Exakte Trefferzahl. Nur bei ?withTotal=true. |
hasMore wird über ein zusätzlich gelesenes Element ermittelt (limit + 1),
nicht über einen Zähl-Query. Deshalb ist total standardmäßig nicht
enthalten: ein exaktes countDocuments kostet auf großen Collections 50–200 ms
pro Request, die die meisten Clients nie lesen. Wenn Sie eine Trefferzahl
anzeigen („1.284 Ergebnisse“), fordern Sie sie mit ?withTotal=true an.
Sortierung und Stabilität
Ohne sort wird nach createdAt absteigend sortiert (neueste zuerst). Mit
sort gilt order, das ohne Angabe asc ist.
Jede Sortierung bekommt serverseitig _id als Tiebreaker angehängt. Das ist
für Offset-Paging zwingend: bei mehreren Zeilen mit identischem Sortierwert
— etwa hunderten Datensätzen aus demselben Import mit gleichem createdAt —
garantiert die Datenbank ohne eindeutigen Tiebreaker keine stabile Reihenfolge
zwischen zwei getrennten Seiten-Requests. Zeilen fielen sonst zwischen die
Seitengrenzen und tauchten nie auf.
Vollständiger Durchlauf
Alle Tickets eines Tenants einlesen, per Cursor:
NEXT=""
while : ; do
RES=$(curl -s "https://os.codemeta.de/api/v1/tickets?limit=200&sort=_id&order=asc${NEXT:+&cursor=$NEXT}" \
-H "X-Tenant-Id: $TENANT" -b cookies.txt)
echo "$RES" | jq -c '.data[]' >> tickets.ndjson
[ "$(echo "$RES" | jq -r '.meta.hasMore')" = "true" ] || break
NEXT=$(echo "$RES" | jq -r '.meta.nextCursor')
done
Abweichende Antwortform bei einzelnen Endpoints
Die generischen Entity-Listen und die Timeline-Route antworten mit meta.
Einige ältere, nicht-generische Endpoints — Benutzer, Einladungen, Tenants,
Sitzungen, Benachrichtigungen sowie die Security- und Vault-Audit-Logs —
liefern stattdessen:
{
"data": [ … ],
"pagination": {
"cursor": "0192f2c0-…",
"hasMore": true
}
}
Inhaltlich ist pagination.cursor dasselbe wie meta.nextCursor. Lesen Sie
defensiv (meta ?? pagination, nextCursor ?? cursor), wenn Ihr Client beide
Endpoint-Familien anspricht. Die Vereinheitlichung auf meta ist vorgesehen;
ein Entfernen von pagination würde vorher über den
Changelog angekündigt.
Grenzen
limitmaximal 200 pro Seite.- Jede Listen-Abfrage hat ein serverseitiges Zeitlimit von 15 Sekunden. Ein Timeout ist praktisch immer ein Filter, der keinen Index trifft — grenzen Sie enger ein, statt das Limit zu erhöhen.
- Für laufende Aktualisierung ist Polling auf Listen der falsche Weg. Nutzen Sie Webhooks und das Listing nur für die Erstbefüllung.