Der Spickzettel zum Seminar Moderne API-Entwicklung mit REST, OpenAPI und Contract Testing, erster Teil: der Entwurf selbst — was eine Ressource ist, welche Methode was zusagt, wie ein Fehler aussieht und wo eine API angegriffen wird. Vertrag, Schema und Contract Testing stehen auf dem zweiten Blatt, Vertrag und Contract Testing. Maßgeblich sind die RFCs selbst — 9110 für HTTP-Semantik, 9111 für Caching, 9457 für Fehler; hier steht die Auswahl, die im Alltag zählt. Das Blatt hält Entscheidungen fest, den Entwurf bringt es nicht bei.
Reichweite einer API
| Reichweite | Consumer | Änderung | Lebensdauer |
|---|---|---|---|
| intern | namentlich bekannt | im Team abstimmbar | Monate |
| Partner | vertraglich gebunden | mit Vorlauf | Jahre |
| öffentlich | unbekannt | nur additiv | offen |
Die Reichweite entscheidet, wie teuer ein Fehlentwurf wird — nicht die Technik dahinter. Sie hängt an den Consumern, nicht an der Netzwerkzone: Ein Dienst im eigenen Cluster ist nicht deshalb intern, weil er dort läuft.
Zugesagt sind in jedem Fall: welche Ressourcen es unter welchen Adressen gibt, welche Felder eine Antwort mindestens enthält, wie Fehler aussehen — und wie lange die heutige Fassung gilt.
Modul: APIs als Produkt und Vertrag verstehen
Ressourcen und Seitenaufteilung
Ressourcen sind Substantive, Zustandsübergänge brauchen kein Verb im Pfad:
GET /abos/{aboId}
GET /abos/{aboId}/lieferungen
GET /lieferfenster?woche=2026-W38
POST /bestellungen
GET /bestellungen/{bestellId}
Was ein eigenes Leben hat, wird eigene Ressource statt Subressource; mehr als zwei, drei Ebenen Verschachtelung macht Adressen unbrauchbar.
| Merkmal | Offset | Cursor |
|---|---|---|
| Aufruf | seite und groesse | cursor aus der Antwort |
| Stabil bei Änderungen | nein, Einträge springen | ja |
| Sprung auf Seite 40 | möglich | nicht vorgesehen |
| Aufwand in der Datenbank | wächst mit dem Offset | bleibt gleich |
Offset ist bequem für Oberflächen mit Seitenzahlen, Cursor belastbar für große Datenmengen. Die nächste Seite gehört in eine Link-Relation nach RFC 8288:
Link: </bestellungen?cursor=b4f1>; rel="next",
</bestellungen>; rel="first"
Die Standardsortierung festlegen — sonst wechselt sie mit dem Ausführungsplan.
Modul: Ressourcen und Endpunkte modellieren
Methoden und Statuscodes
| Methode | sicher | idempotent | cachebar |
|---|---|---|---|
| GET | ja | ja | ja |
| HEAD | ja | ja | ja |
| POST | nein | nein | nur explizit |
| PUT | nein | ja | nein |
| PATCH | nein | nein | nein |
| DELETE | nein | ja | nein |
PATCH steht nicht in RFC 9110, sondern in RFC 5789 — und ist weder sicher noch idempotent. DELETE dagegen muss idempotent sein: Der zweite Aufruf ist kein Fehler.
Die Statuscodes, die im Alltag den Unterschied machen:
| Code | Bedeutung |
|---|---|
| 201 / 202 / 204 | angelegt / angenommen, noch nicht erledigt / kein Inhalt |
| 400 | Syntax kaputt |
| 401 / 403 | nicht angemeldet / angemeldet und trotzdem nicht erlaubt |
| 409 | Konflikt mit dem aktuellen Zustand |
| 412 / 428 | Vorbedingung verletzt / Vorbedingung gefordert |
| 415 / 406 | gesendeter Rumpf abgelehnt / nichts Passendes lieferbar |
| 422 | formal korrekt, fachlich unmöglich |
| 429 | zu viele Anfragen |
Lange Vorgänge: 202 bestätigt die Annahme und verweist auf eine Statusressource, 303 See Other führt nach Abschluss auf das Ergebnis.
Modul: HTTP-Semantik korrekt einsetzen
ETags und optimistische Nebenläufigkeit
GET /touren/71 -> 200 OK
ETag: "v7"
PUT /touren/71
If-Match: "v7" -> 204 No Content
PUT /touren/71
If-Match: "v7" -> 412 Precondition Failed
Ohne If-Match gewinnt der zweite Schreiber, und die Änderung des ersten verschwindet unbemerkt. Ein starker Validator ändert sich bei jeder Änderung der Repräsentation, ein schwacher darf gleich bleiben, wenn nur Unwesentliches sich ändert: If-Match vergleicht stark, If-None-Match darf schwach vergleichen. Caching regelt RFC 9111, nicht 9110.
Ein ETag aus einem Zeitstempel ist innerhalb einer Sekunde blind; eines über die ganze Collection ändert sich ständig.
Modul: HTTP-Semantik korrekt einsetzen
Fehler nach RFC 9457
| Element | Inhalt | Beständigkeit |
|---|---|---|
| type | URI als Kennung des Problemtyps | fest, ist der Schlüssel |
| status | HTTP-Statuscode, nur zur Bequemlichkeit | wie die Antwort |
| title | kurze Zusammenfassung für Menschen | ändert sich nicht |
| detail | Erklärung dieses Vorfalls | je Vorfall |
| instance | URI dieses einen Vorfalls | je Vorfall |
Consumer unterscheiden Fehler am type, nie am title — der darf übersetzt
werden.
{
"type": "/probleme/kontingent",
"title": "Das Lieferfenster ist ausgebucht.",
"status": 409,
"detail": "Für den 18.09.2026 sind 0 Kisten frei.",
"instance": "/bestellungen/9f2c",
"naechstesFenster": "2026-09-25T08:00:00+02:00"
}
Medientyp ist application/problem+json, der type darf eine relative Referenz
sein, Erweiterungsfelder sind erlaubt — unbekannte muss ein Consumer ignorieren.
Nicht das Richtige sind Problem Details, wenn der Statuscode allein alles sagt
oder die Branche ein etabliertes Fehlerformat hat.
Modul: Requests, Responses und Fehler robust gestalten
Repräsentationen
{
"bestellId": "9f2c",
"aboId": "a-4471",
"lieferfenster": "2026-09-18T08:00:00+02:00",
"kistenGroesse": "mittel",
"betrag": { "wert": "24.90", "waehrung": "EUR" },
"status": "bestaetigt"
}
Vier Entscheidungen, die man einmal trifft und überall durchhält: Beträge als
Zeichenkette mit Währung, damit die Rundung nicht vom Consumer abhängt;
Zeitstempel mit Zone; Aufzählungswerte unübersetzt; und ein optionales Feld
entweder immer weg oder immer null — nicht mal so, mal so.
Den Idempotenzschlüssel liefert der Aufrufer, nicht der Server — sonst löst er nichts. Wiederholungen brauchen eine Wartezeit, sonst verstärken sie die Überlast.
Modul: Requests, Responses und Fehler robust gestalten
OWASP API Security Top 10 (2023)
| Nr. | Risiko |
|---|---|
| API1 | Broken Object Level Authorization |
| API2 | Broken Authentication |
| API3 | Broken Object Property Level Authorization |
| API4 | Unrestricted Resource Consumption |
| API5 | Broken Function Level Authorization |
| API6 | Unrestricted Access to Sensitive Business Flows |
| API7 | Server Side Request Forgery |
| API8 | Security Misconfiguration |
| API9 | Improper Inventory Management |
| API10 | Unsafe Consumption of APIs |
API9 ist ein Vertragsthema: Nicht dokumentierte Endpunkte werden nicht geprüft und nicht abgeschaltet, alte Versionen laufen weiter, weil niemand ihren Betrieb bemerkt. Der Vertrag ist das Inventar — wenn er vollständig ist.
Berechtigung hat drei Ebenen, und alle drei werden serverseitig geprüft:
| Ebene | Frage |
|---|---|
| Funktion | Darf diese Rolle diese Operation überhaupt aufrufen? |
| Objekt | Gehört dieses konkrete Objekt zu diesem Aufrufer? |
| Eigenschaft | Darf dieser Aufrufer genau dieses Feld lesen oder schreiben? |
Die Eigenschaftsebene ist die, die am häufigsten fehlt — das Schema muss die erlaubten Felder abschließend nennen, sonst entscheidet der Aufrufer:
POST /bestellungen
{
"aboId": "a-4471",
"kistenGroesse": "gross",
"rabattSatz": 90
}
Modul: API-Sicherheit in Vertrag und Design
Typische Fallen
- Alles antwortet mit 200 und einem Statusfeld im Rumpf — der Consumer muss dann jede Antwort auspacken, um Erfolg von Fehler zu unterscheiden.
- 500 für fachliche Ablehnungen. Weckt nachts die Bereitschaft für einen Fall, der genau so vorgesehen war.
- POST zum Lesen verhindert jede Zwischenspeicherung.
- Der Server liefert ETags, prüft If-Match aber nie. Sieht korrekt aus und schützt nichts.
- Antworten pauschal als nicht cachebar markiert, aus Angst vor Zwischenspeichern.
- Jeder Endpunkt erfindet sein eigenes Fehlerformat — und
detailwird zum Feld, das der Consumer parsen muss. - Stacktraces in
detailverraten Interna nach außen. - Die URI trägt die Tabellenstruktur nach außen und friert sie ein.
- Die Seitengröße ist frei wählbar, und ein Aufruf holt die gesamte Datenbank.
- Die Objektprüfung fehlt, weil die ID schwer zu erraten scheint.
- Ein Endpunkt gilt als sicher, weil er im internen Netz liegt.
- 404 statt 403 gewählt, aber die Antwortzeit verrät die Existenz weiterhin.
Zum Seminar Moderne API-Entwicklung mit REST, OpenAPI und Contract Testing