Eine gut entworfene API ist eine Freude in der Nutzung; eine schlecht entworfene wird zu einem Rätsel, das man bei jeder Anfrage neu lösen muss. In diesem Artikel sammle ich die REST API best practices, die in der täglichen Entwicklung wirklich helfen — von der Benennung der Ressourcen über HTTP-Methoden bis hin zu Statuscodes und Versionierung. Das Ziel ist eine konsistente, vorhersehbare Schnittstelle, die andere Entwickler (und du selbst in sechs Monaten) erraten können, ohne die Dokumentation zu öffnen.
Ressourcen benennen: Substantive, keine Verben
Die Grundidee von REST ist, das System rund um Ressourcen zu modellieren. Endpoint-Pfade sollten Substantive enthalten, keine Verben, und für Sammlungen den Plural verwenden. Die Aktion drückt die HTTP-Methode aus; sie in den Pfad zu schreiben ist redundant.
- Gut:
GET /users,GET /users/42,POST /users - Schlecht:
GET /getUsers,POST /createUser,GET /user/list
Zeige bei verschachtelten Ressourcen die Beziehung im Pfad: Die Bestellungen eines Nutzers lesen sich natürlich als GET /users/42/orders. Verschachtle aber nicht mehr als etwa zwei Ebenen tief; Pfade wie /users/42/orders/7/items/3/reviews werden schwer wartbar. Biete stattdessen direkten Zugriff auf die Unterressource: GET /order-items/3. Verwende Kleinbuchstaben und Bindestriche in URLs (/order-items) und vermeide Unterstriche oder camelCase.
HTTP-Methoden ihrer Bedeutung entsprechend einsetzen
Jede Methode hat einen expliziten Vertrag, und ihn einzuhalten lässt zwischengeschaltete Komponenten (Caches, Proxys, Browser) sich korrekt verhalten.
- GET — liest eine Ressource, ohne Seiteneffekte (sicher und idempotent).
- POST — erstellt eine neue Ressource; nicht idempotent (zweimal aufgerufen entstehen zwei Datensätze).
- PUT — ersetzt die gesamte Ressource; idempotent (dieselbe Anfrage zu wiederholen ändert das Ergebnis nicht).
- PATCH — aktualisiert einen Teil der Ressource.
- DELETE — löscht die Ressource; idempotent.
Praktische Regel: GET darf niemals Daten verändern. Eine „Löschen"-Aktion auf einem Endpoint wie GET /users/42/delete ist verbreitet, aber falsch, denn ein Browser-Prefetch oder ein Crawler könnte den Datensatz unbemerkt löschen. Bevorzuge PUT für eine vollständige und PATCH für eine teilweise Aktualisierung.
Die richtigen Statuscodes zurückgeben
Der Statuscode ist das erste Signal dafür, was passiert ist, noch bevor der Antwort-Body gelesen wird. Immer 200 OK zurückzugeben und {"success": false} in den Body zu schreiben, macht den Client blind. Die Codes, die du am häufigsten verwendest:
- 200 OK — erfolgreiches GET/PUT/PATCH.
- 201 Created — eine Ressource wurde per POST erstellt; gib ihre Adresse im
Location-Header zurück. - 204 No Content — Erfolg ohne Body (meist DELETE).
- 400 Bad Request — die Anfrage ist fehlerhaft oder hat die Validierung nicht bestanden.
- 401 Unauthorized — nicht authentifiziert; 403 Forbidden — authentifiziert, aber nicht berechtigt.
- 404 Not Found — die Ressource existiert nicht.
- 409 Conflict — ein Konflikt (z. B. eine doppelte E-Mail).
- 422 Unprocessable Entity — Syntax gültig, aber semantische Validierung fehlgeschlagen (viele Frameworks nutzen dies für Validierungsfehler).
- 429 Too Many Requests — Ratenlimit überschritten.
- 500 Internal Server Error — ein unerwarteter serverseitiger Fehler.
Nutze den Unterschied zwischen 401 und 403 korrekt: Der erste bedeutet „Ich weiß nicht, wer du bist", der zweite „Ich weiß, wer du bist, aber du darfst das nicht".
Konsistente, maschinenlesbare Fehler
Jeden Fehler in derselben Struktur zurückzugeben, erlaubt dem Client, eine einzige Fehlerbehandlungsroutine zu schreiben. Liefere eine menschenlesbare message, einen stabilen Maschinen-code und Validierungsdetails pro Feld:
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": {
"code": "validation_failed",
"message": "Die übermittelten Daten sind ungültig.",
"fields": {
"email": ["Gib eine gültige E-Mail-Adresse ein."],
"age": ["Muss 18 oder älter sein."]
}
}
}
Halte das Feld code stabil und sprachunabhängig (validation_failed) und verwende message für Text, den du einem Nutzer zeigen kannst. Gib in der Produktion niemals die internen Details von 500-Fehlern preis (Stack Traces, SQL); gib stattdessen eine request_id zurück, damit der Nutzer dem Support zugeordnet werden kann.
Versionierung: Änderungen verwalten, ohne sie zu brechen
Ab dem Moment, in dem deine API live ist, bricht jede Änderung, die die Abwärtskompatibilität verletzt, bestehende Clients. Deshalb sollte eine Versionierungsstrategie von Anfang an festgelegt werden. Der gängigste und sichtbarste Ansatz ist, die Version in den Pfad zu setzen:
GET /v1/users
GET /v2/users
Alternativ kannst du die Version in einem Header transportieren (Accept: application/vnd.api+json; version=2); das gilt als „reiner", ist aber schwerer zu entdecken und zu testen. Für die meisten Teams ist die URL-basierte Versionierung praktisch und klar. Das Kernprinzip: Ein Feld hinzuzufügen ist meist nicht brechend, aber ein Feld zu entfernen, umzubenennen oder seinen Typ zu ändern ist brechend und erfordert eine neue Version. Veröffentliche einen Deprecation-Zeitplan für alte Versionen.
Listen-Endpoints: Paginierung, Filterung, Sortierung
Eine Sammlung unverändert zurückzugeben funktioniert bei kleinen Datensätzen, aber je größer die Tabelle wird, desto mehr überfordert es Server und Client. Füge Listen-Endpoints von Anfang an Paginierung hinzu. Es gibt zwei gängige Ansätze:
- Offset-basiert:
GET /users?page=3&per_page=20— einfach, erlaubt das Springen zu einer Seitennummer; driftet aber, wenn sich die Daten häufig ändern. - Cursor-basiert:
GET /users?limit=20&cursor=eyJpZCI6MTQ0fQ— stabiler und performanter bei großen, sich häufig ändernden Datensätzen.
Drücke Filterung und Sortierung mit Query-Parametern aus: GET /users?status=active&sort=-created_at. Ein vorangestelltes - in der Sortierung ist eine gängige Konvention für absteigende Reihenfolge. Die Gesamtzahl und die Information zur nächsten Seite in einem meta-Block zurückzugeben, erleichtert dem Client die Navigation.
Ein paar weitere Prinzipien
- HTTPS überall: Eine API, die Tokens und personenbezogene Daten transportiert, darf nur über TLS laufen.
- JSON-Konsistenz: Halte dich an einen einzigen Stil für Feldnamen (
snake_caseodercamelCase) und ändere ihn nicht über die API hinweg. - ISO 8601 und UTC für Datumsangaben: Das Format
2026-06-27T14:30:00Zbeseitigt Mehrdeutigkeit. - Dokumentation: Veröffentliche ein maschinenlesbares Schema mit OpenAPI (Swagger), das dir sowohl Dokumentation als auch die Generierung von Client-Code liefert.
- Ratenbegrenzung und Authentifizierung: Füge Rate Limiting hinzu, um Missbrauch zu verhindern, und melde dessen Zustand mit den passenden Headern.
Häufige Fragen
Sollte ich PUT oder PATCH verwenden?
Verwende PUT, wenn du die gesamte Ressource sendest und ersetzt; verwende PATCH, wenn du nur die wenigen Felder sendest, die sich ändern. PUT ist idempotent: denselben vollständigen Body mehrfach zu senden ändert das Ergebnis nicht. PATCH dient teilweisen Aktualisierungen, und sein Body enthält nur die zu ändernden Felder.
Sollte ich die Version in der URL oder in einem Header halten?
Beides ist gültig. URL-basierte Versionierung (/v1/...) ist die Wahl der meisten Teams, weil sie leicht zu entdecken und im Browser zu testen ist. Header-basierte Versionierung ist näher an der REST-Reinheit, aber umständlicher in Bezug auf Tooling-Unterstützung und Sichtbarkeit. Solange du konsistent bleibst, sind beide korrekt.
Kann ich nicht einfach für alles 200 zurückgeben und Fehler im Body signalisieren?
Nein. HTTP-Statuscodes existieren genau dafür; Cache-Schichten, Proxys und Client-Bibliotheken handeln danach. 4xx/5xx für Fehler zu verwenden lässt den Client die richtige Entscheidung treffen, ohne den Antwort-Body zu parsen, und es verbessert die Beobachtbarkeit.
Möchtest du deine API von Grund auf entwerfen oder bestehende Endpoints auf den Standard heben? Von der Ressourcenmodellierung über Versionierung bis zur Dokumentation kann ich dir helfen, eine saubere, konsistente REST-Schnittstelle aufzubauen. Nimm Kontakt auf und lass uns die API deines Projekts gemeinsam härten.