Eine API ist ein dauerhaftes Versprechen
Eine API verbindet Systeme, Teams und häufig auch Unternehmen. Sobald mehrere Clients darauf aufbauen, wird jede unklare Änderung teuer. Robustheit bedeutet deshalb nicht nur, dass ein Endpoint erreichbar ist. Ein Client muss verstehen können, welche Daten erwartet werden, welche Antworten möglich sind und wie er auf Fehler reagieren soll.
Der Vertrag sollte unabhängig von einer konkreten Implementierung beschrieben werden. OpenAPI bietet dafür eine sprachunabhängige Beschreibung von HTTP-Schnittstellen. Sie kann Dokumentation, Tests und Client-Generierung unterstützen. Entscheidend ist jedoch, dass der Vertrag tatsächlich gepflegt und gegen die Implementierung geprüft wird.
Ressourcen und HTTP-Semantik respektieren
Gute APIs orientieren sich an fachlichen Ressourcen und verwenden HTTP-Methoden entsprechend ihrer Bedeutung. GET liest, POST stößt typischerweise eine Erstellung oder Aktion an, PUT ersetzt einen Zustand und PATCH verändert Teile davon. Diese Semantik erleichtert Caching, Wiederholungen und die Arbeit von Clients.
Nicht jede fachliche Operation muss künstlich als CRUD dargestellt werden. Eine explizite Aktion kann verständlicher sein als ein schwer lesbares Update mit versteckter Bedeutung. Wichtig ist, dass der Name die fachliche Absicht beschreibt und die Antwort konsistent bleibt.
openapi: 3.1.0
info:
title: Order API
version: 1.0.0
paths:
/orders/{orderId}:
get:
operationId: getOrder
parameters:
- in: path
name: orderId
required: true
schema:
type: string
responses:
'200':
description: Order found
'404':
description: Order not found
Fehler als eigenes Datenmodell behandeln
Ein HTTP-Status allein reicht selten aus. Clients benötigen einen stabilen Fehlercode, eine verständliche Nachricht und gegebenenfalls feldbezogene Details. Interne Stacktraces, Datenbankmeldungen oder Klassennamen gehören nicht in öffentliche Antworten.
{
"error": {
"code": "ORDER_STATE_CONFLICT",
"message": "Die Bestellung kann in diesem Status nicht storniert werden.",
"requestId": "req_8f27c4",
"details": {
"currentState": "shipped"
}
}
}
Der maschinenlesbare Code bleibt stabil, während die Nachricht übersetzt oder verbessert werden kann. Eine Request-ID verbindet die Client-Antwort mit Logs und Traces. Validierungsfehler sollten betroffene Felder eindeutig benennen, damit Benutzeroberflächen sie korrekt darstellen können.
Idempotenz bewusst planen
Netzwerkfehler führen dazu, dass Clients nicht sicher wissen, ob eine Anfrage verarbeitet wurde. Bei Zahlungen, Bestellungen oder Provisionierungen darf ein erneuter Versuch keine doppelte Aktion auslösen. Idempotenzschlüssel oder fachlich eindeutige Referenzen machen Wiederholungen kontrollierbar.
- Der Client sendet einen stabilen Schlüssel pro fachlicher Operation.
- Der Server speichert Ergebnis und Status der ersten Verarbeitung.
- Ein Wiederholungsversuch liefert dasselbe Ergebnis zurück.
- Schlüssel besitzen einen klaren Gültigkeitszeitraum.
- Parallel eintreffende Anfragen werden atomar behandelt.
Versionierung ist mehr als eine URL
Eine neue Hauptversion ist notwendig, wenn bestehende Clients ihr Verhalten ändern müssen. Viele Erweiterungen können dagegen kompatibel umgesetzt werden: neue optionale Felder, zusätzliche Endpoints oder neue Werte, sofern Clients unbekannte Daten tolerieren. Eine Versionsnummer in der URL löst nicht das eigentliche Problem, wenn alte Verträge sofort abgeschaltet werden.
Zu einer Versionierungsstrategie gehören Deprecation-Hinweise, Nutzungsdaten, Migrationsdokumentation und ein realistischer Abschaltplan. APIs sollten messen können, welche Clients alte Felder oder Versionen noch verwenden.
Timeouts, Wiederholungen und Grenzen definieren
Jede externe Anfrage braucht ein Timeout. Ohne Grenze kann ein langsamer Dienst Threads, Worker oder Verbindungen blockieren. Wiederholungen sind nur bei geeigneten Fehlern sinnvoll und benötigen Abstände sowie eine maximale Anzahl. Andernfalls verstärken sie eine Störung.
- Kurze Verbindungs- und Antwort-Timeouts definieren.
- Nur temporäre Fehler wiederholen.
- Backoff und zufällige Streuung verwenden.
- Maximale Payload-, Seiten- und Batchgrößen festlegen.
- Rate Limits verständlich kommunizieren.
Vertragstests und Beobachtbarkeit
Automatisierte Tests sollten nicht nur erfolgreiche Antworten prüfen. Wichtiger sind Grenzfälle, falsche Berechtigungen, veraltete Versionen, doppelte Anfragen und Teilausfälle. Contract-Tests stellen sicher, dass Provider und Consumer dieselben Erwartungen besitzen.
In Produktion werden Latenz, Fehlerquote, Statuscodes und Abhängigkeiten beobachtet. Eine API kann technisch verfügbar sein und trotzdem fachlich fehlerhafte Daten liefern. Deshalb gehören fachliche Kennzahlen wie abgelehnte Bestellungen oder unvollständige Synchronisationen ebenfalls in das Monitoring.
Fazit
Robuste APIs verbinden klare Verträge mit kontrollierbarem Fehlerverhalten. Idempotenz, Versionierung und Beobachtbarkeit müssen von Anfang an Teil des Designs sein. So bleiben Schnittstellen auch dann verlässlich, wenn mehrere Clients, Teams und Releases gleichzeitig weiterentwickelt werden.
