6. Schnittstellen entwerfen
Schnittstellen entwerfen
Abschnitt betitelt „Schnittstellen entwerfen“Bisher waren wir Konsumenten fremder Schnittstellen; ab jetzt wechseln wir die Seite. Der Lehrplan verlangt, Schnittstellen zur Kommunikation zwischen Anwendungen zu entwerfen und zu implementieren, und die Reihenfolge ist ernst gemeint: Erst der Entwurf, dann die Implementierung. Eine Schnittstelle ist ein Vertrag, und Verträge schreibt man, bevor man baut; nachträgliche Vertragsänderungen beschädigen jeden, der schon konsumiert. In diesem Kapitel entsteht eine vollständige API auf Papier, die in den Next.js-Kapiteln dann wirklich implementiert wird.
Als Entwurfsstil verwenden wir REST (Representational State Transfer), den De-facto-Standard für Web-APIs. REST ist kein Gesetz, sondern eine Sammlung von Konventionen; ihr Wert liegt in der Vorhersehbarkeit: Wer eine REST-API kennt, findet sich in der nächsten sofort zurecht.
Ressourcen: das Substantiv-Prinzip
Abschnitt betitelt „Ressourcen: das Substantiv-Prinzip“REST denkt in Ressourcen: den Dingen, die die API verwaltet. Als durchgehendes Beispiel dieses und der folgenden Kapitel dient eine kleine Render-Farm für Videoaufträge; ihre Ressourcen sind etwa Jobs, Presets und Ergebnisdateien. (In den Next.js-Kapiteln wird daraus das Projekt render-farm, in den Übungen wenden Sie dieselben Prinzipien auf eine andere Domäne an, das Schulbuffet.) Aus den Ressourcen ergeben sich die URLs, und zwar nach festen Regeln:
- URLs benennen Ressourcen als Substantive im Plural:
/api/jobs,/api/presets. - Eine Sammlung ist
/api/jobs, ein Element daraus/api/jobs/42. - Die Aktion steckt in der HTTP-Methode, niemals in der URL:
DELETE /api/jobs/42statt/api/deleteJob?id=42. - Zugehörigkeit darf sich verschachteln:
/api/jobs/42/logist das Log von Job 42. Mehr als eine Ebene Verschachtelung wird schnell unhandlich; dann lieber flach bleiben und filtern.
Damit ergibt sich für eine Ressource das Standard-Quintett:
| Methode + Pfad | Bedeutung | Erfolgsstatus |
|---|---|---|
GET /api/jobs | alle Jobs auflisten | 200 |
POST /api/jobs | neuen Job anlegen | 201 Created |
GET /api/jobs/42 | einen Job lesen | 200 |
PATCH /api/jobs/42 | einen Job ändern | 200 |
DELETE /api/jobs/42 | einen Job löschen | 204 No Content |
Feinheiten, die eine API professionell machen: POST beantwortet das Anlegen mit 201 und liefert die neue Ressource (samt vergebener ID) im Body zurück; DELETE liefert 204 ohne Body; ein GET auf eine unbekannte ID liefert 404. Wer diese Konventionen einhält, erspart jedem Konsumenten das Rätselraten.
Filtern, Sortieren und Blättern laufen über Query-Parameter der Sammlung, nicht über neue Pfade:
GET /api/jobs?state=failedGET /api/jobs?sort=createdAt&order=descGET /api/jobs?limit=20&offset=40JSON als Datenvertrag
Abschnitt betitelt „JSON als Datenvertrag“Die zweite Hälfte des Vertrags sind die Datenformate. Für jede Operation wird festgelegt, wie Anfrage- und Antwort-Body aussehen, und zwar verbindlich und mit Beispielen. Für den Render-Job:
{ "id": 42, "filename": "intro.mp4", "preset": "web-1080p", "state": "running", "progress": 0.62, "createdAt": "2027-03-14T09:31:02Z"}Beim Anlegen (POST /api/jobs) schickt der Client nur, was er wissen kann:
{ "filename": "intro.mp4", "preset": "web-1080p" }id, state, progress und createdAt vergibt der Server; das Antwortformat ist wieder der vollständige Job. Diese Trennung (Eingabeformat gegen Ausgabeformat) ist eine bewusste Vertragsentscheidung und gehört dokumentiert.
Formatregeln, die sich bewährt haben und in diesem Jahr verbindlich sind:
- Feldnamen in
camelCase, konsistent über die ganze API. - Zeitstempel im ISO-8601-Format als String (
"2027-03-14T09:31:02Z", das Z markiert UTC); niemals lokale Formate wie14.03.2027. - Zustandsfelder als dokumentierte Wertemenge (
queued | running | done | failed), im Code ein Union Type. - IDs sind für den Client undurchschaubare Kennungen; ob Zahl oder String, entscheidet der Anbieter einmal und bleibt dabei.
- Kein Über-Verschachteln: Antworten so flach wie fachlich möglich.
In TypeScript wird der Vertrag zu Interfaces, die später Client und Server importieren; eine Quelle der Wahrheit statt zweier Abschriften:
export interface Job { id: number; filename: string; preset: string; state: "queued" | "running" | "done" | "failed"; progress: number; createdAt: string;}
export type NewJob = Pick<Job, "filename" | "preset">;(Pick baut aus einem Interface ein neues mit ausgewählten Feldern; einer der wenigen TypeScript-Hilfstypen, die sich früh lohnen.)
Fehler sind Teil des Vertrags
Abschnitt betitelt „Fehler sind Teil des Vertrags“Eine API, die nur den Erfolgsfall dokumentiert, ist halb entworfen. Zum Vertrag gehört ein konsistentes Fehlerformat, das für jede Fehlerantwort der ganzen API gilt:
{ "error": { "code": "VALIDATION_FAILED", "message": "preset 'web-1080' is unknown", "details": [{ "field": "preset", "issue": "unknown value" }] }}Dazu die Zuordnung der Statuscodes: 400 für ungültige Anfragen (mit details, die dem Client sagen, was zu korrigieren ist), 404 für unbekannte Ressourcen, 409 Conflict für fachliche Konflikte (etwa: Job läuft bereits und kann nicht gelöscht werden), 500 für eigene Fehler des Servers, dann ohne interne Details wie Stack Traces, die Angreifern helfen und Konsumenten nichts nützen. Der maschinenlesbare code erlaubt dem Client gezielte Reaktionen, die message ist für Menschen.
Stabilität und Versionierung
Abschnitt betitelt „Stabilität und Versionierung“Der Wert eines Vertrags ist seine Haltbarkeit. Deshalb die Grundregeln der API-Evolution:
- Additive Änderungen sind erlaubt: ein neues optionales Feld in der Antwort, ein neuer Endpunkt. Bestehende Konsumenten ignorieren, was sie nicht kennen, und laufen weiter.
- Brechende Änderungen sind verboten: Feld umbenennen oder entfernen, Typ ändern, Statuscode-Verhalten ändern. Wer so etwas braucht, veröffentlicht eine neue Version und lässt die alte weiterlaufen; die übliche Form ist das Pfadpräfix
/api/v1/...gegen/api/v2/....
Für die Schulprojekte genügt Version 1 und die Disziplin, sie nicht zu brechen; das Prinzip zu kennen gehört aber zum Entwurfshandwerk, und es erklärt, warum öffentliche APIs Versionsnummern in ihren Pfaden tragen.
API-Dokumentation: der Vertrag in Schriftform
Abschnitt betitelt „API-Dokumentation: der Vertrag in Schriftform“Der Entwurf wird als Dokument festgehalten, pro Endpunkt nach festem Schema. Das Format ist bewusst schlicht (Markdown genügt); entscheidend ist die Vollständigkeit:
### POST /api/jobs
Legt einen neuen Render-Job an.
Request-Body: { "filename": string, "preset": string }- filename: Name der Quelldatei, nicht leer- preset: eines der Presets aus GET /api/presets
Antworten:- 201 Created: der vollständige Job (siehe Job-Format), Header "Location: /api/jobs/{id}"- 400 VALIDATION_FAILED: filename leer oder preset unbekannt- Beispiel-Request und Beispiel-Antwort: siehe untenBeispiele sind Pflicht, für Erfolgs- und Fehlerfall: Sie sind der Teil der Doku, den Konsumenten wirklich lesen, und sie zwingen den Autor, den eigenen Entwurf einmal konkret durchzuspielen. Professionelle Ökosysteme formalisieren solche Beschreibungen (das Format OpenAPI ist der verbreitete Standard, aus dem sich Doku-Seiten und Client-Code generieren lassen); für dieses Jahr ist das strukturierte Markdown-Dokument das Lernziel, der Blick auf OpenAPI der Ausblick.
Der Entwurf wird im Team reviewt, bevor implementiert wird, mit denselben Regeln wie jedes Review: Kritik nennt das verletzte Kriterium. Prüffragen: Sind die URLs Substantive und konsistent? Passen Methoden und Statuscodes zu den Konventionen? Ist jedes Feld typisiert und jedes Beispiel gültig? Deckt das Fehlerformat alle dokumentierten Fehlerfälle? Kann ein Konsument allein aus diesem Dokument einen Client schreiben? Die letzte Frage ist der eigentliche Test: Wenn ja, ist der Vertrag vollständig.
Lernergebnisse: Was Sie nach diesem Kapitel können sollten
Abschnitt betitelt „Lernergebnisse: Was Sie nach diesem Kapitel können sollten“Nach Abschluss dieses Kapitels sollten Schülerinnen und Schüler in der Lage sein:
- Erklären: das Ressourcen-Prinzip von REST (Substantiv-URLs, Methoden als Aktionen, Statuscodes als Ergebnis) erklären.
- Entwerfen: für eine gegebene Domäne die Ressourcen, Endpunkte und Query-Parameter einer API festlegen.
- Entwerfen: Request- und Response-Formate als JSON-Verträge mit Beispielen definieren und als TypeScript-Interfaces festhalten.
- Entwerfen: ein konsistentes Fehlerformat samt Statuscode-Zuordnung für die ganze API festlegen.
- Erklären: additive von brechenden Änderungen unterscheiden und das Prinzip der Versionierung erläutern.
- Beurteilen: fremde API-Entwürfe anhand nachvollziehbarer Kriterien reviewen und Lücken im Vertrag aufdecken.
Passende Übungen
Abschnitt betitelt „Passende Übungen“- Aufgabe 10 - API auf Papier