JSON API Best Practices for Developers
Design better JSON APIs with consistent naming, structured responses, proper pagination, error handling, and performance optimizations used by top engineering teams.
Warum API-Design wichtig ist
Ein gut gestaltetes JSON-API ist der Unterschied zwischen einer Integration, die einen Nachmittag dauert, und einer, die eine Woche in Anspruch nimmt. Gutes API-Design reduziert Support-Tickets, macht Ihre Dokumentation kürzer und ermöglicht es den Nutzern, vorherzusagen, wie Dinge funktionieren, bevor sie die Dokumentation lesen.
Die Muster in diesem Leitfaden sind nicht theoretisch — sie stammen von APIs bei Stripe, GitHub, Slack und anderen Unternehmen, die für hervorragende Entwicklererfahrungen bekannt sind. Lassen Sie uns jedes einzelne durchgehen.
Namenskonventionen
Die beiden dominierenden Stile für JSON-Eigenschaftsnamen sind camelCase und snake_case. Beides ist in Ordnung, solange Sie sich für eines entscheiden und es überall verwenden.
| Konvention | Beispiel | Verwendet von | Ökosystem |
|---|---|---|---|
camelCase | firstName, createdAt | Google APIs, Azure | JavaScript / TypeScript-Frontends |
snake_case | first_name, created_at | Stripe, GitHub, Slack | Python / Ruby-Backends |
Richtlinie: Wählen Sie die Konvention, die zu Ihrem primären Verbraucher-Ökosystem passt. Wenn Ihr API hauptsächlich von JavaScript-Frontends genutzt wird, vermeidet camelCase Reibungen. Wenn Ihr Backend Python oder Ruby ist und das API B2B ist, ist snake_case idiomatisch.
Was auch immer Sie wählen, seien Sie absolut konsistent. Das Mischen von user_name und firstName in derselben Antwort untergräbt das Vertrauen schneller als fast alles andere.
Konsistente Antwortstruktur
Jede Antwort von Ihrem API sollte einem vorhersehbaren Rahmen folgen. Hier ist ein Muster, das für die meisten APIs funktioniert:
Erfolgsantwort
{
"data": {
"id": "usr_abc123",
"email": "alice@example.com",
"name": "Alice Johnson",
"created_at": "2025-01-15T09:30:00Z"
},
"meta": {
"request_id": "req_7f2a9c"
}
}
Listenantwort
{
"data": [
{ "id": "usr_abc123", "name": "Alice Johnson" },
{ "id": "usr_def456", "name": "Bob Smith" }
],
"meta": {
"total": 142,
"page": 1,
"per_page": 20,
"request_id": "req_8b3d1e"
}
}
Fehlerantwort
{
"error": {
"code": "validation_error",
"message": "Der Anfrageinhalt enthält ungültige Felder.",
"details": [
{
"field": "email",
"message": "Muss eine gültige E-Mail-Adresse sein."
},
{
"field": "age",
"message": "Muss mindestens 13 Jahre alt sein."
}
]
},
"meta": {
"request_id": "req_4c8e2a"
}
}
Das Schlüsselprinzip: data ist bei Erfolg vorhanden, error ist bei Misserfolg vorhanden, und meta erscheint in beiden für die Anforderungsverfolgung und Paginierung. Verbraucher können einen einzigen Antwort-Handler schreiben, der zuerst nach error sucht.
Versuchen Sie es selbst: Fügen Sie eine API-Antwort in unseren JSON Formatter ein, um die Struktur zu inspizieren und Inkonsistenzen zu erkennen.
Paginierungsmuster
Die meisten Listenendpunkte benötigen eine Paginierung. Es gibt drei gängige Ansätze, jeder mit unterschiedlichen Vor- und Nachteilen:
1. Offset-Paginierung
Der einfachste Ansatz. Der Client gibt eine Seitenzahl oder einen Offset an:
GET /api/users?page=3&per_page=20
{
"data": [...],
"meta": {
"total": 142,
"page": 3,
"per_page": 20,
"total_pages": 8
}
}
2. Cursor-Paginierung
Anstelle von Seitenzahlen gibt der Server einen undurchsichtigen Cursor zurück, der auf die nächste Ergebnismenge zeigt:
GET /api/users?limit=20&after=cursor_abc123
{
"data": [...],
"meta": {
"has_more": true,
"next_cursor": "cursor_def456"
}
}
3. Keyset-Paginierung
Verwendet einen echten Spaltenwert (wie einen Zeitstempel oder eine ID) als Cursor. Dies ist Cursor-Paginierung ohne die Undurchsichtigkeit:
GET /api/users?limit=20&created_after=2025-01-15T09:30:00Z
{
"data": [...],
"meta": {
"has_more": true,
"next_created_after": "2025-01-16T14:22:00Z"
}
}
Paginierungsvergleich
| Aspekt | Offset | Cursor | Keyset |
|---|---|---|---|
| Springe zu Seite N | Ja | Nein | Nein |
| Gesamtanzahl | Einfach einzufügen | Teuer | Teuer |
| Leistung bei großen Tabellen | Verschlechtert sich (OFFSET scan rows) | Konsistent | Konsistent |
| Handhabt Einfügungen/Löschungen | Kann Elemente überspringen oder duplizieren | Stabil | Stabil |
| Implementierungskomplexität | Niedrig | Mittel | Mittel |
| Verwendet von | Traditionelle Web-Apps | Stripe, Slack | Twitter / X |
Richtlinie: Verwenden Sie die Offset-Paginierung für Admin-Dashboards und interne Tools, bei denen Benutzer zu bestimmten Seiten springen müssen. Verwenden Sie Cursor- oder Keyset-Paginierung für öffentliche APIs, unendliche Scroll-UIs und jede Datensatz, der sich häufig ändert.
Fehlerbehandlung
Gute Fehlerantworten sagen dem Verbraucher was schiefgelaufen ist, wo und idealerweise wie man es behebt. Hier ist die Struktur, die wir empfehlen:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Sie haben das Limit von 100 Anfragen pro Minute überschritten.",
"details": [],
"doc_url": "https://docs.example.com/errors/rate-limit"
}
}
Der code ist eine maschinenlesbare Zeichenfolge (nicht ein HTTP-Statuscode — dieser gehört in die tatsächliche HTTP-Antwort). Die message ist menschenlesbar. Das details-Array bietet feldspezifische Fehler für Validierungsfehler. Und doc_url verlinkt auf die Dokumentation über den Fehler.
Häufige Fehlercodes und deren HTTP-Statuszuordnungen:
| HTTP-Status | Fehlercode | Wann zu verwenden |
|---|---|---|
| 400 | validation_error | Anfrageinhalt erfüllt nicht die Schema-Validierung |
| 401 | unauthorized | Fehlende oder ungültige Authentifizierung |
| 403 | forbidden | Authentifiziert, hat aber keine Berechtigung |
| 404 | not_found | Ressource existiert nicht |
| 409 | conflict | Duplizierte Ressource oder Statuskonflikt |
| 422 | unprocessable_entity | Gültiges JSON, aber semantisch falsch |
| 429 | rate_limit_exceeded | Zu viele Anfragen |
| 500 | internal_error | Unerwarteter Serverfehler |
Datentypen und Formatierung
Die richtigen Datentypen zu verwenden, verhindert eine ganze Kategorie von Integrationsfehlern:
Daten und Zeiten
Verwenden Sie immer das ISO 8601-Format mit Zeitzoneninformationen:
{
"created_at": "2025-01-15T09:30:00Z",
"expires_at": "2025-02-15T23:59:59+05:30"
}
Verwenden Sie niemals Unix-Zeitstempel in Antwortkörpern — sie sind nicht menschenlesbar und schaffen Zeitzonenambiguität. Wenn Sie Unix-Zeitstempel benötigen (z. B. für JWT-Ansprüche), dokumentieren Sie dies klar.
Große Zahlen
Der Number-Typ von JavaScript verliert die Präzision über 2^53 - 1 (9.007.199.254.740.991). Wenn Ihre IDs oder Geldwerte diesen Wert überschreiten, verwenden Sie Strings:
{
"id": "9223372036854775807",
"amount": "99.99",
"currency": "USD"
}
Stripe, Twitter und Discord verwenden aus genau diesem Grund String-IDs.
Null vs. Fehlend
Definieren Sie eine klare Konvention und dokumentieren Sie sie:
null— Das Feld existiert, hat aber keinen Wert (z. B. der Benutzer hat noch keine Biografie festgelegt).- Fehlender Schlüssel — Das Feld ist für diese Ressource nicht relevant oder wurde nicht angefordert (z. B. in einer partiellen Antwort).
Viele APIs enthalten null-Felder, um eine konsistente Form zu bewahren, was es den Clients erleichtert, typisierte Schnittstellen zu schreiben, ohne überall optionale Verkettungen zu verwenden.
Booleans
Verwenden Sie echte JSON-Boolean-Werte, nicht Strings oder Ganzzahlen:
// Gut
{ "is_active": true }
// Schlecht
{ "is_active": "true" }
{ "is_active": 1 }
Versionierungsstrategien
APIs entwickeln sich weiter. Sie benötigen eine Strategie, um Änderungen vorzunehmen, ohne bestehende Verbraucher zu brechen. Die drei Hauptansätze:
- URL-Versionierung:
/v1/users,/v2/users. Der häufigste und sichtbarste Ansatz. Verwendet von Stripe, GitHub und Twilio. Einfach zu verstehen, einfach zu routen, kann jedoch zu Code-Duplikation führen. - Header-Versionierung:
Accept: application/vnd.api+json;version=2. Sauberere URLs, aber weniger entdeckbar. Verwendet von GitHub (als Alternative) und Azure. - Abfrageparameter:
/users?version=2. Einfach, aber verunreinigt die Abfragezeichenfolge. In der Praxis weniger verbreitet.
Richtlinie: URL-Versionierung ist der pragmatische Standard. Es ist explizit, cachebar und funktioniert mit jedem HTTP-Client. Deviieren Sie nur, wenn Sie einen spezifischen technischen Grund haben.
Kompression
JSON ist Text und komprimiert außergewöhnlich gut. Aktivieren Sie immer die Kompression für API-Antworten:
| Algorithmus | Kompressionsverhältnis | Geschwindigkeit | Browserunterstützung |
|---|---|---|---|
| gzip | Gut (70–80% Reduktion) | Schnell | Universell |
| Brotli (br) | Besser (75–85% Reduktion) | Etwas langsamer zu komprimieren | Alle modernen Browser |
Für eine typische JSON-Antwort von 50KB reduziert gzip sie auf etwa 10–15KB, und Brotli auf etwa 8–12KB. Der Server sollte den Accept-Encoding-Header respektieren und entsprechend wählen. Die meisten Reverse-Proxys (Nginx, Cloudflare) erledigen dies automatisch.
Leistungstipps
Partielle Antworten (Feldauswahl)
Lassen Sie Verbraucher nur die Felder anfordern, die sie benötigen. Dies reduziert die Payload-Größe und die Datenbanklast:
GET /api/users?fields=id,name,email
{
"data": [
{ "id": "usr_abc123", "name": "Alice", "email": "alice@example.com" }
]
}
Google APIs nennt dies "partielle Antworten" und unterstützt es über alle Endpunkte hinweg. Es ist besonders wertvoll für mobile Clients mit langsamen Verbindungen.
Sparse Fieldsets
Für APIs, die verwandte Ressourcen zurückgeben, lassen Sie Verbraucher steuern, welche Felder für jeden Ressourcentyp enthalten sind:
GET /api/articles?include=author&fields[article]=title,body&fields[author]=name
Dies ist ein Muster aus der JSON:API-Spezifikation. Es beseitigt das N+1-Abfrageproblem auf der Client-Seite, während die Payloads minimal bleiben.
ETags und bedingte Anfragen
Geben Sie einen ETag-Header mit Antworten zurück. Clients können If-None-Match bei nachfolgenden Anfragen senden, und Sie geben 304 Not Modified (leerer Körper) zurück, wenn sich nichts geändert hat. Dies spart Bandbreite und Verarbeitungszeit für Ressourcen, die sich nicht häufig ändern.
Zusammenfassungs-Checkliste
Bevor Sie Ihr API veröffentlichen, gehen Sie diese Checkliste durch:
- Konsistente Namenskonvention (
camelCaseodersnake_case) über alle Endpunkte hinweg. - Vorhersehbarer Antwortrahmen mit
data,errorundmeta. - Paginierung auf allen Listenendpunkten mit dokumentierten Grenzen.
- Strukturierte Fehlerantworten mit maschinenlesbaren Codes, Nachrichten und feldspezifischen Details.
- ISO 8601-Daten, String-IDs für große Zahlen, echte Booleans.
- Dokumentierte und durchgesetzte API-Versionierungsstrategie.
- gzip- oder Brotli-Kompression aktiviert.
- Feldauswahl oder sparse Fieldsets für leistungssensitive Verbraucher.
Versuchen Sie es selbst: Formatieren und validieren Sie Ihre API-Antworten mit unserem JSON Formatter, um sicherzustellen, dass sie einer sauberen, konsistenten Struktur folgen.