8 Patterns für REST-APIs, die man nicht erklären muss
Die ersten vier Patterns betreffen Struktur und Semantik einer API, die letzten vier Konsistenz und Weiterentwicklung. Jede Regel mit Gegenbeispiel und Empfehlung.
1. Ressourcen statt Aktionen
Die URL benennt die Ressource, die HTTP-Methode die Operation.
// Falsch — die Aktion steckt in der URL
POST /getUsers
POST /createOrder
POST /deleteProduct/42
// Richtig — eine Ressource trägt beliebig viele Operationen
GET /users
POST /orders
DELETE /products/42
HTTP beschreibt die Aktion bereits. Wer sie zusätzlich in die URL schreibt, sagt dasselbe zweimal.
2. URLs vorhersagbar gestalten
Eine Namenskonvention wählen und überall durchhalten.
// Falsch — jeder Endpoint funktioniert für sich, keiner lässt sich erraten
/user
/customers
/customer_profiles
/getCustomerOrders
// Richtig — Collection, Element, Sub-Collection nach demselben Muster
/users
/users/42
/users/42/orders
/customer-profiles
Welche Konvention man wählt, ist zweitrangig. Dass man sie durchhält, nicht — eine vorhersagbare API braucht weniger Erklärung.
Für Listen gilt immer der Plural (/users), POST /users erstellt genau eine Ressource und liefert 201 Created mit einem Location-Header auf die neue Ressource. Sollen mehrere Datensätze auf einmal angelegt werden, ist ein eigener Endpoint (POST /users/batch) mit Ergebnisliste pro Datensatz sauberer als ein Array am selben Endpoint — dort bleibt sonst unklar, welcher Location-Header und welcher Statuscode bei Teilfehlern gelten.
3. HTTP-Methoden zweckdienlich nutzen
GET liest, POST erzeugt, PUT ersetzt, PATCH ändert, DELETE entfernt.
// Falsch — der Client muss Hausregeln statt HTTP-Semantik lernen
POST /users/42/update
GET /users/42/delete
// Richtig — Standard-Semantik, keine Überraschungen
GET /users/42 // liest
PUT /users/42 // ersetzt
PATCH /users/42 // ändert
DELETE /users/42 // entfernt
Wichtig für Idempotenz: GET, PUT und DELETE dürfen wiederholt werden, ohne den Zustand erneut zu verändern. Zwei identische POST-Requests erzeugen dagegen zwei Ressourcen — entscheidend, wenn ein Client nach einem Timeout erneut sendet.
4. Statuscodes aussagekräftig gestalten
Das Ergebnis gehört in den Statuscode, nicht nur in den Body.
// Falsch — Header meldet Erfolg, Body einen Fehler
HTTP/1.1 200 OK
{ "success": false, "error": "Product not found" }
// Richtig — Statuscode und Body sagen dasselbe
HTTP/1.1 404 Not Found
{ "code": "PRODUCT_NOT_FOUND", "message": "Produkt 42 …" }
Erfolg: 200 gelesen · 201 erstellt · 202 asynchron angenommen · 204 ohne Body. Fehler: 400 ungültig · 401 nicht authentifiziert · 403 keine Berechtigung · 404 nicht gefunden · 409 Konflikt · 422 Validierung · 429 Rate Limit.
5. Fehlerantworten konsistent halten
Der Statuscode liefert die Kategorie, der Body die Details.
// Falsch — nicht auswertbar
{ "error": "Something went wrong" }
// Richtig — Message für Menschen, Code für Software, Felder fürs Formular
{
"code": "VALIDATION_FAILED",
"message": "E-Mail ungültig",
"status": 422,
"fields": ["email"]
}
Das genaue Format darf jedes Team selbst wählen — solange jede Fehlerantwort der API gleich aussieht.
6. Nicht alles gehört in den Pfad
Filter, Suche und Sortierung sind Query-Parameter.
// Falsch — der Pfad wächst unkontrolliert, eine Löschung
// versteckt sich hinter einem GET
GET /products/electronics/in-stock/under-500/sort/price
GET /products?action=delete
// Richtig — der Pfad bleibt stabil, Parameter verfeinern nur
GET /products?category=electronics&inStock=true&maxPrice=500&sort=price
DELETE /products/42
Der Pfad identifiziert die Ressource, Query-Parameter verfeinern sie, die HTTP-Methode beschreibt die Operation.
7. API-Änderungen sorgfältig behandeln
Erst prüfen, ob die Änderung überhaupt etwas bricht.
// Ohne Version wird aus
"price": 49.99
// eine andere Struktur — bestehende Clients brechen
"pricing": { "amount": 49.99, "currency": "EUR" }
// Richtig — v1 bleibt während der Migration bestehen
/v1/products → "price": 49.99
/v2/products → "pricing": { … }
Ein zusätzliches optionales Feld braucht meist keine neue Version. Ist Versionierung nötig, dann eine Strategie — URL oder Header — konsequent nutzen und den Konsumenten einen Migrationspfad geben.
8. Formate konsistent halten
Der nächste Endpoint soll sich vertraut anfühlen.
// Falsch — drei gültige, aber gemischte Schreibweisen
/orders "createdAt"
/users "created_at"
/products "created at"
?page= / ?offset= / ?p=
// Richtig — ein Schema, überall identisch
"createdAt": "2026-09-13T08:00:00Z"
?page=2&limit=50
Ein Naming-Schema, ein Datumsformat, eine Pagination, eine Fehlerstruktur. Das Ziel ist kein dickes Regelbuch, sondern dass sich von einem Endpoint auf alle anderen schließen lässt.
Einordnung: Was heißt „REST-konform“?
Das Richardson Maturity Model beschreibt vier Stufen: Stufe 0 (ein Endpoint, alles läuft als POST — klassisches RPC/SOAP), Stufe 1 (jede Ressource bekommt eine eigene URL), Stufe 2 (Methoden und Statuscodes werden zweckgemäß genutzt, Idempotenz inklusive — hier stehen die acht Patterns) und Stufe 3 (HATEOAS: die Antwort enthält Links auf die nächsten möglichen Schritte statt sie im Client zu verdrahten).
REST ist kein Protokoll, sondern ein Satz von Architektur-Constraints aus Roy Fieldings Dissertation (2000): Client-Server-Trennung, Zustandslosigkeit, Cachebarkeit, Schichtensystem und eine einheitliche Schnittstelle. Streng genommen gehört HATEOAS dazu. In der Praxis ist eine benutzbare Stufe-2-API aber mehr wert als eine dogmatisch korrekte, die niemand versteht.
Was hier noch nicht drin war
Themen, die in der Praxis mindestens so oft wehtun wie die acht Patterns:
- Zustandslosigkeit — keine Server-Session, jeder Request trägt seine Authentifizierung selbst. Voraussetzung dafür, dass sich einfach weitere Instanzen dazustellen lassen.
- Caching mit ETags — ein Fingerabdruck der Repräsentation, den der Client mit
If-None-Matchzurückschickt; passt er noch, antwortet der Server mit304 Not Modifiedohne Body. - Optimistic Locking — derselbe ETag mit
If-Matchbeim Schreiben, sonst412 Precondition Failed. Verhindert, dass zwei parallele Updates sich gegenseitig überschreiben. - Idempotency-Keys — ein vom Client erzeugter Schlüssel im Header, damit ein wiederholter
POSTnach einem Timeout nicht zwei Bestellungen anlegt (von Stripe populär gemacht). - Cursor statt Offset beim Blättern —
?page=2zählt Positionen und liefert bei bewegten Daten Einträge doppelt oder gar nicht. Ein Cursor (?after=<token>) zeigt auf einen Datensatz und bleibt stabil. - Rate Limiting —
429zusammen mitRetry-After, damit Clients wissen, wann sie wiederkommen dürfen. - Lange Operationen —
202 Acceptedplus Status-URL zum Pollen oder ein Webhook, statt den Request minutenlang offen zu halten.
Und: nicht jede API muss REST sein — GraphQL eignet sich, wenn Clients sehr unterschiedliche Ausschnitte brauchen, gRPC bei interner Service-zu-Service-Kommunikation mit hohem Durchsatz.
Fazit
Eine API wird nicht gut, weil sie REST-konform ist. Sie wird gut, wenn Clients sie benutzen können, ohne ständig in die Doku zu sehen: Ressourcennamen ergeben Sinn, Methoden verhalten sich vorhersagbar, Statuscodes bedeuten etwas, Fehler haben eine feste Struktur, Filter funktionieren überall gleich, Änderungen brechen keine Clients.
Zuletzt aktualisiert: