Ratgeber · JSON 2026
JSON in REST-APIs: application/json, Versionen, Fehler-Schema
RFC 7807 (Problem Details) ist das Standard-Error-Format. application/problem+json statt eigener Konstrukte.
JSON ist heute der API-Default
99 % aller neu gebauten REST-APIs sprechen JSON. Die Content-Type-Header und Konventionen sind seit RFC 7159 (2014) und RFC 8259 (2017) stabil. Trotzdem gibt es ein paar Best-Practices, die nicht jeder kennt.
Pflicht-Header
| Header | Wert | Wann |
|---|---|---|
| Content-Type | application/json | bei Request- und Response-Bodies |
| Accept | application/json | bei Client-Requests |
| Content-Type (Fehler) | application/problem+json | bei RFC 7807 Problem-Details |
Wichtig: kein Charset-Suffix nötig - JSON ist immer UTF-8 nach RFC 8259.
RFC 7807: das Standard-Error-Format
Statt einer eigenen Error-Struktur sollte man RFC 7807 (Problem Details for HTTP APIs) nutzen:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://example.com/probs/validation",
"title": "Validation failed",
"status": 400,
"detail": "The email field must be a valid address.",
"instance": "/orders/12345",
"errors": [
{ "field": "email", "code": "invalid_format" }
]
}
Pflichtfelder: type (URI-Referenz auf eine Doku-Seite), title (kurze Beschreibung), status (HTTP-Code). Plus beliebige Extensions. Wird von Spring, FastAPI, NestJS und Co. nativ unterstützt.
Versionierung
Drei verbreitete Varianten - keine ist offiziell empfohlen, alle drei haben ihre Anhänger:
| Stil | Beispiel | Vorteil | Nachteil |
|---|---|---|---|
| URL-Pfad | /api/v2/orders | extrem klar sichtbar | Code-Pollution mit Versions-Strings |
| Header | X-API-Version: 2 | URLs bleiben sauber | nicht in Browser-URL sichtbar |
| Accept-Vendor | application/vnd.acme.v2+json | RFC-konform | komplex zu testen mit curl |
Pragmatische Empfehlung: URL-Pfad, weil 90 % der API-Konsumenten Browser-/Postman-User sind und das einfacher debuggen können.
Pagination-Conventions
Drei populäre Patterns für JSON-Pagination:
- Link-Header (RFC 5988): rel="next", rel="prev" - GitHub-Stil, kein Body-Overhead
- Envelope mit meta:
{ data: [...], meta: { total, page, perPage, hasNext } } - Cursor-basiert: opake String als nextCursor - robust gegen Inserts
Status-Codes & Body-Pattern
| Status | Body |
|---|---|
| 200 OK | Daten oder Envelope |
| 201 Created | erzeugte Ressource + Location-Header |
| 204 No Content | leer (auch für DELETE) |
| 400 Bad Request | Problem Details (RFC 7807) |
| 401 Unauthorized | Problem Details |
| 404 Not Found | Problem Details |
| 422 Unprocessable Entity | Problem Details mit field-level errors |
| 500 Internal Server Error | generisches Problem Details, keine Stack-Traces |
Date-Format
ISO 8601 mit Zeitzone. Konvention: UTC mit "Z"-Suffix wenn keine Lokalzeit wichtig ist.
"createdAt": "2026-05-14T12:34:56Z"
"birthDate": "1990-03-15" // nur Datum, ohne Zeit
Keine Unix-Timestamps in der Public-API - sie sind weniger lesbar und brauchen Konventionen für Sekunden vs Millisekunden.
Nullable Fields
Vier Optionen einen "leeren" Wert zu repräsentieren:
- Key komplett weglassen
- Key mit null
- Key mit leerem String
- Key mit leerem Array oder Object
Konvention: 1 und 2 sind semantisch unterschiedlich. "Key fehlt" = "nicht bekannt", "Key=null" = "bewusst leer". 3 und 4 nur wenn Typ stabil bleiben muss. Im JSON Schema mit "type": ["string", "null"] explizit machen.