API Pagination Design: Millionen Datensätze effizient verwalten

Ashley Innocent

Ashley Innocent

13 March 2026

API Pagination Design: Millionen Datensätze effizient verwalten

enterprise.banner.title

enterprise.banner.feature1

enterprise.banner.feature2

enterprise.banner.feature3

enterprise.banner.ctaB

TL;DR

Für große Datensätze verwenden Sie Cursor-basierte oder Keyset-Paginierung anstelle der Offset-basierten Paginierung. Offset-Paginierung (?page=1&limit=20) funktioniert bei Millionen von Datensätzen schlecht und führt zu Dateninkonsistenzen. Die moderne PetstoreAPI implementiert Cursor-basierte Paginierung mit undurchsichtigen Token und HATEOAS-Links für effiziente, konsistente Ergebnisse.

Einleitung

Ihre API gibt eine Liste von Haustieren zurück. Sie haben 10 Millionen Haustiere in der Datenbank. Ein Client fragt GET /pets?page=500000&limit=20 an. Ihre Datenbank führt OFFSET 10000000 LIMIT 20 aus. Die Abfrage dauert 30 Sekunden. Ihre API läuft in einen Timeout.

Dies ist das Problem der Offset-Paginierung. Sie funktioniert gut für kleine Datensätze, bricht aber bei Skalierung zusammen. Die Datenbank muss Millionen von Zeilen scannen, um den Offset zu erreichen, obwohl Sie nur 20 Ergebnisse zurückgeben.

Der alte Swagger Petstore behandelt Paginierung überhaupt nicht. Die moderne PetstoreAPI implementiert Cursor-basierte Paginierung, die auf Millionen von Datensätzen mit konsistenter Leistung skaliert.

💡
Wenn Sie REST-APIs erstellen oder testen, hilft Ihnen Apidog dabei, das Paginierungsverhalten zu testen, Antwortformate zu validieren und sicherzustellen, dass Ihre API große Datensätze korrekt verarbeitet. Sie können Paginierungsszenarien simulieren, Grenzfälle testen und die Leistung überprüfen.

App herunterladen

In diesem Leitfaden erfahren Sie, warum die Offset-Paginierung fehlschlägt, wie die Cursor-basierte Paginierung funktioniert und wie die moderne PetstoreAPI eine effiziente Paginierung implementiert.

Warum die Offset-Paginierung bei Skalierung fehlschlägt

Offset-Paginierung ist der gängigste Ansatz, hat aber ernsthafte Probleme.

Wie Offset-Paginierung funktioniert

GET /pets?page=1&limit=20    → OFFSET 0 LIMIT 20
GET /pets?page=2&limit=20    → OFFSET 20 LIMIT 20
GET /pets?page=3&limit=20    → OFFSET 40 LIMIT 20

Die Datenbank überspringt offset Zeilen und gibt limit Zeilen zurück.

Problem 1: Leistung verschlechtert sich mit der Seitenzahl

Seite 1:

SELECT * FROM pets OFFSET 0 LIMIT 20;
-- Schnell: scannt 20 Zeilen

Seite 1000:

SELECT * FROM pets OFFSET 20000 LIMIT 20;
-- Langsam: scannt 20.020 Zeilen, gibt 20 zurück

Seite 500.000:

SELECT * FROM pets OFFSET 10000000 LIMIT 20;
-- Sehr langsam: scannt 10.000.020 Zeilen, gibt 20 zurück

Die Datenbank muss alle Zeilen bis zum Offset scannen, obwohl sie diese verwirft. Die Leistung verschlechtert sich linear mit der Seitenzahl.

Problem 2: Inkonsistente Ergebnisse

Während ein Client durch die Ergebnisse blättert, ändern sich die Daten:

Anfrage 1:

GET /pets?page=1&limit=2

Gibt zurück: [Haustier A, Haustier B]

Jemand fügt Haustier Z hinzu (wird alphabetisch zuerst sortiert)

Anfrage 2:

GET /pets?page=2&limit=2

Gibt zurück: [Haustier B, Haustier C] ← Haustier B erscheint zweimal!

Haustier B erschien auf beiden Seiten, weil ein neues Haustier eingefügt wurde. Umgekehrt können Haustiere übersprungen werden, wenn Löschungen vorgenommen werden.

Problem 3: Tiefe Paginierung ist teuer

Benutzer gehen selten über Seite 10 hinaus. Aber wenn Ihre API ?page=1000000 zulässt, müssen Sie damit umgehen. Tiefe Paginierungsabfragen sind teuer und können für Denial-of-Service-Angriffe verwendet werden.

Wann Offset-Paginierung akzeptabel ist

Offset-Paginierung funktioniert gut für:

Für öffentliche APIs oder große Datensätze verwenden Sie Cursor-basierte Paginierung.

Cursor-basierte Paginierung erklärt

Cursor-basierte Paginierung verwendet einen undurchsichtigen Token, um die Position im Ergebnisdatensatz zu markieren.

Funktionsweise

Anfrage 1:

GET /pets?limit=20

Antwort 1:

{
  "data": [...],
  "pagination": {
    "nextCursor": "eyJpZCI6IjAxOWI0MTMyLTcwYWEtNzY0Zi1iMzE1LWUyODAzZDg4MmEyNCJ9",
    "hasMore": true
  }
}

Anfrage 2:

GET /pets?cursor=eyJpZCI6IjAxOWI0MTMyLTcwYWEtNzY0Zi1iMzE1LWUyODAzZDg4MmEyNCJ9&limit=20

Der Cursor ist ein undurchsichtiger Token (üblicherweise base64-kodiert), der die Position kodiert. Der Client analysiert ihn nicht – er gibt ihn einfach zurück.

Vorteile

1. Konsistente Leistung

Die Datenbank verwendet einen Index, um die Cursor-Position direkt zu finden:

SELECT * FROM pets
WHERE id > '019b4132-70aa-764f-b315-e2803d882a24'
ORDER BY id
LIMIT 20;

Diese Abfrage ist unabhängig von der Position im Datensatz schnell. Sie verwendet eine Indexsuche, keinen Scan.

2. Konsistente Ergebnisse

Cursor sind stabil. Wenn sich Daten zwischen Anfragen ändern, erhalten Sie immer noch konsistente Ergebnisse. Neue Datensätze verursachen keine Duplikate oder Übersprünge.

3. Keine Angriffe durch tiefe Paginierung

Clients können nicht zu beliebigen Positionen springen. Sie müssen sequenziell paginieren, was Missbrauch einschränkt.

Cursor-Format

Cursor sind typischerweise base64-kodiertes JSON:

// Dekodierter Cursor
{
  "id": "019b4132-70aa-764f-b315-e2803d882a24",
  "createdAt": "2026-03-13T10:30:00Z"
}

Der Cursor enthält genügend Informationen, um die Paginierung fortzusetzen. Für die moderne PetstoreAPI umfasst dies die Ressourcen-ID und das Sortierfeld.

Keyset-Paginierung für sortierte Daten

Keyset-Paginierung ist eine Variante der Cursor-basierten Paginierung für sortierte Daten.

Funktionsweise

Anstelle eines undurchsichtigen Cursors verwenden Sie den letzten Wert der vorherigen Seite:

Anfrage 1:

GET /pets?limit=20&sortBy=createdAt

Antwort 1:

{
  "data": [
    {"id": "...", "createdAt": "2026-03-13T10:00:00Z"},
    ...
    {"id": "...", "createdAt": "2026-03-13T10:30:00Z"}
  ]
}

Anfrage 2:

GET /pets?limit=20&sortBy=createdAt&after=2026-03-13T10:30:00Z

Der Parameter after verwendet den letzten createdAt-Wert der vorherigen Seite.

SQL-Abfrage

SELECT * FROM pets
WHERE created_at > '2026-03-13T10:30:00Z'
ORDER BY created_at
LIMIT 20;

Dies ist effizient, da ein Index auf created_at verwendet wird.

Wann Keyset-Paginierung zu verwenden ist

Die moderne PetstoreAPI verwendet standardmäßig Cursor-basierte Paginierung, unterstützt aber Keyset-Paginierung für Zeitreihendaten.

Wie die moderne PetstoreAPI Paginierung implementiert

Die moderne PetstoreAPI verwendet Cursor-basierte Paginierung mit HATEOAS-Links.

Anfrageformat

GET /pets?limit=20
GET /pets?cursor={token}&limit=20

Parameter:

Antwortformat

{
  "data": [
    {
      "id": "019b4132-70aa-764f-b315-e2803d882a24",
      "name": "Fluffy",
      "species": "CAT"
    }
  ],
  "pagination": {
    "limit": 20,
    "hasMore": true,
    "nextCursor": "eyJpZCI6IjAxOWI0MTMyLTcwYWEtNzY0Zi1iMzE1LWUyODAzZDg4MmEyNCJ9"
  },
  "links": {
    "self": "https://petstoreapi.com/pets?limit=20",
    "next": "https://petstoreapi.com/pets?cursor=eyJpZCI6IjAxOWI0MTMyLTcwYWEtNzY0Zi1iMzE1LWUyODAzZDg4MmEyNCJ9&limit=20"
  }
}

Hauptmerkmale

1. Undurchsichtige Cursor

Cursor sind base64-kodiert. Clients analysieren sie nicht.

2. HATEOAS-Links

Das links-Objekt stellt gebrauchsfertige URLs bereit. Clients müssen keine Paginierungs-URLs konstruieren.

3. hasMore-Flag

Zeigt an, ob weitere Ergebnisse vorhanden sind. Clients wissen, wann sie mit der Paginierung aufhören müssen.

4. Limit-Validierung

Das maximale Limit beträgt 100. Verhindert, dass Clients riesige Seiten anfordern.

Die vollständigen Details finden Sie in der Paginierungsdokumentation der modernen PetstoreAPI.

Paginierungs-Antwortformat

Die moderne PetstoreAPI verpackt paginierte Antworten in einer konsistenten Struktur.

Sammlungs-Wrapper

{
  "data": [...],
  "pagination": {...},
  "links": {...}
}

Warum Sammlungen verpacken?

  1. Erweiterbarkeit - Kann Metadaten hinzufügen, ohne Clients zu beeinträchtigen
  2. Konsistenz - Alle paginierten Endpunkte verwenden dasselbe Format
  3. HATEOAS - Links führen Clients durch die Paginierung

Paginierungs-Metadaten

"pagination": {
  "limit": 20,
  "hasMore": true,
  "nextCursor": "...",
  "totalCount": 1000  // Optional, teuer zu berechnen
}

totalCount ist optional, da die Berechnung bei großen Datensätzen teuer ist. Die meisten Clients benötigen es nicht.

Paginierung mit Apidog testen

Apidog hilft Ihnen, das Paginierungsverhalten umfassend zu testen.

Testszenarien

1. Erste Seite

GET /pets?limit=20

Erwartung: 20 Ergebnisse, hasMore=true, nextCursor vorhanden

2. Folgeseiten

GET /pets?cursor={token}&limit=20

Erwartung: 20 Ergebnisse, hasMore=true/false, nextCursor vorhanden/nicht vorhanden

3. Letzte Seite

GET /pets?cursor={lastToken}&limit=20

Erwartung: < 20 Ergebnisse, hasMore=false, kein nextCursor

4. Leere Ergebnisse

GET /pets?status=NONEXISTENT&limit=20

Erwartung: 0 Ergebnisse, hasMore=false, kein nextCursor

5. Limit-Validierung

GET /pets?limit=1000

Erwartung: 400 Bad Request (überschreitet maximales Limit)

Apidog-Testkonfiguration

// Test: Paginierungsstruktur
pm.test("Response has pagination", () => {
  pm.expect(pm.response.json()).to.have.property('pagination');
  pm.expect(pm.response.json().pagination).to.have.property('hasMore');
});

// Test: HATEOAS-Links
pm.test("Response has links", () => {
  const links = pm.response.json().links;
  pm.expect(links).to.have.property('self');
  if (pm.response.json().pagination.hasMore) {
    pm.expect(links).to.have.property('next');
  }
});

Die richtige Paginierungsstrategie wählen

Verschiedene Strategien passen zu unterschiedlichen Anwendungsfällen.

Offset-Paginierung

Verwenden Sie, wenn:

Verwenden Sie nicht, wenn:

Cursor-basierte Paginierung

Verwenden Sie, wenn:

Verwenden Sie nicht, wenn:

Keyset-Paginierung

Verwenden Sie, wenn:

Verwenden Sie nicht, wenn:

Empfehlung der modernen PetstoreAPI: Verwenden Sie Cursor-basierte Paginierung für öffentliche APIs und große Datensätze.

Fazit

Paginierung ist entscheidend für APIs, die große Datensätze zurückgeben. Offset-Paginierung ist einfach, skaliert aber nicht. Cursor-basierte Paginierung bietet konsistente Leistung und zuverlässige Ergebnisse für Millionen von Datensätzen.

Die moderne PetstoreAPI implementiert Cursor-basierte Paginierung mit undurchsichtigen Token, HATEOAS-Links und geeigneten Metadaten. Dieses Design skaliert effizient und bietet eine großartige Entwicklererfahrung.

Testen Sie Ihre Paginierungsimplementierung mit Apidog, um sicherzustellen, dass sie Grenzfälle behandelt, Limits validiert und konsistente Ergebnisse zurückgibt.

Wichtige Erkenntnisse:

App herunterladen

FAQ

Warum nicht einfach alle Ergebnisse ohne Paginierung zurückgeben?

Das Zurückgeben von Millionen von Datensätzen in einer einzigen Antwort führt zu Speicherproblemen, langsamem Netzwerktransfer und einer schlechten Benutzererfahrung. Paginierung ist für große Datensätze unerlässlich.

Können Clients mit Cursor-Paginierung zu einer bestimmten Seite springen?

Nein, Cursor-Paginierung erfordert sequenziellen Zugriff. Wenn wahlfreier Zugriff erforderlich ist, sollten Sie die Offset-Paginierung für kleine Datensätze in Betracht ziehen oder stattdessen eine Suche/Filterung implementieren.

Wie gehe ich mit Paginierung und Filterung um?

Fügen Sie Filterparameter in Paginierungsanfragen ein: GET /pets?status=AVAILABLE&cursor={token}&limit=20. Der Cursor kodiert sowohl die Position als auch den Filterstatus.

Sollte ich die Gesamtzahl in Paginierungsantworten aufnehmen?

Nur wenn Clients es benötigen und Ihr Datensatz klein ist. Die Berechnung der Gesamtzahl ist bei großen Datensätzen teuer (erfordert eine separate COUNT-Abfrage).

Wie implementiere ich Cursor-Paginierung in SQL?

Verwenden Sie eine WHERE-Klausel mit dem Cursor-Wert: SELECT * FROM pets WHERE id > ? ORDER BY id LIMIT 20. Stellen Sie sicher, dass Sie einen Index auf der Sortierspalte haben.

Was, wenn meine Cursor-Token ungültig werden?

Geben Sie 400 Bad Request mit einer Fehlermeldung zurück. Cursor können ungültig werden, wenn Daten gelöscht werden oder der Paginierungsstatus abläuft.

Wie lange sollten Cursor gültig bleiben?

Cursor der modernen PetstoreAPI sind unbegrenzt gültig, solange die referenzierte Ressource existiert. Einige APIs lassen Cursor nach 24 Stunden ablaufen.

Kann ich Cursor-Paginierung mit mehreren Sortierfeldern verwenden?

Ja, aber der Cursor muss alle Sortierfelder kodieren. Dies macht Cursor komplexer. Erwägen Sie stattdessen die Verwendung eines einzelnen zusammengesetzten Sortierschlüssels.

Praktizieren Sie API Design-First in Apidog

Entdecken Sie eine einfachere Möglichkeit, APIs zu erstellen und zu nutzen