Zum Hauptinhalt springen

AB 03 – Die API dokumentieren

Die Situation

Eine Partnerfirma soll das Gästebuch in ihr eigenes Portal einbinden. Ihre Entwicklerin schreibt:

„Schickt mir bitte eine Beschreibung der Schnittstelle. Welche Endpunkte gibt es, welche Felder erwartet ihr, welche Statuscodes kommen zurück?"

Du könntest das in ein Textdokument schreiben. Die Frage ist nur: Wer pflegt es, wenn sich die Schnittstelle ändert?

Lernziele

Nach Bearbeitung dieses Arbeitsblatts kannst du:

  • erklären, warum handgeschriebene Schnittstellendokumentation veraltet
  • OpenAPI als Beschreibungsformat für REST-Schnittstellen einordnen
  • springdoc einbinden und die Swagger-UI benutzen
  • Endpunkte mit Annotationen aussagekräftiger dokumentieren

Das Problem mit Dokumentation

Jede Dokumentation, die getrennt vom Code gepflegt wird, läuft ihm irgendwann hinterher. Jemand ergänzt ein Feld, jemand ändert einen Statuscode — und niemand denkt an die Beschreibung.

Die Lösung: Die Dokumentation aus dem Code erzeugen. Was aus Pfaden, Methoden und Feldern abgeleitet wird, kann dann nicht mehr veralten — es hat keine eigene Quelle, die jemand pflegen müsste.

Was OpenAPI ist

OpenAPI ist ein standardisiertes Format, um REST-Schnittstellen zu beschreiben — welche Pfade es gibt, welche Methoden, welche Felder, welche Antworten. Die Beschreibung selbst ist eine Datei in JSON oder YAML; springdoc liefert beides aus (/v3/api-docs und /v3/api-docs.yaml).

Weil es ein Standard ist, können Werkzeuge etwas damit anfangen: eine Bedienoberfläche daraus bauen, Client-Code erzeugen, Tests ableiten.

Früher hieß das Format Swagger. Der Name lebt in den Werkzeugen weiter — daher „Swagger-UI".

Damit schließt sich auch ein Kreis zum Infoblatt Das REST-Paradigma: Dort stand, dass die Praxis auf Stufe 2 des Reifegradmodells arbeitet und die Aufgabe, dem Client die Möglichkeiten mitzuteilen, von der Dokumentation übernommen wird. Genau das passiert hier.

Aufgaben

Aufgabe 1 – springdoc einbinden

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.1.0</version>
</dependency>
Warum hier eine Version steht

Bei den Spring-Boot-Startern konntest du die Version weglassen, weil der Eltern-POM sie vorgibt. springdoc gehört nicht zu Spring Boot — deshalb musst du sie hier angeben.

Achte darauf, eine Version zu wählen, die zu deiner Spring-Boot-Version passt: Die 3.x-Linie gehört zu Spring Boot 4, die 2.x-Linie zu Spring Boot 3. Nachschlagen im Infoblatt Maven und Abhängigkeiten.

springdoc.swagger-ui.path=/swagger-ui.html

Aufgabe 2 – Die Oberfläche ansehen

Du hast keine Zeile Dokumentation geschrieben — und siehst trotzdem alle deine Endpunkte, mit Methoden, Pfaden, Parametern und Feldern.

Das vorausgefüllte Beispiel geht schief — und das ist lehrreich

Du bekommst eine 500. Der Grund steht im Rumpf, den Swagger dir angeboten hat:

{
"id": 0,
"title": "string",
"comment": "string",
"author": "string",
"date": "2026-09-03T08:43:05.489Z"
}

Beim Datum steht bei dir eine andere Uhrzeit — Swagger setzt dort den aktuellen Zeitpunkt ein. Der Rest ist überall gleich.

Das Feld id gehört da nicht hinein. Wer eine Id mitschickt, behauptet damit: „Diesen Eintrag gibt es schon." save() versucht daraufhin kein INSERT, sondern ein UPDATE — und findet die Zeile nicht. In der Konsole:

StaleObjectStateException: Row was already updated or deleted by another
transaction for entity [de.szut.gaestebuch.model.GuestbookEntry with id '0']

Das Feld date gehört auch nicht hinein — es richtet nur keinen Schaden an. Probier es aus: Schick einen Eintrag mit "date": "2020-05-17T12:00:00" und ohne id. Du bekommst 201, und im Datum steht trotzdem jetzt. @CreationTimestamp überschreibt, was der Client geschickt hat.

Warum steht das überhaupt im Beispiel?

springdoc kennt nur eine Klasse: deine Entität. Und die hat nun einmal ein Feld id und ein Feld date. Dass der Client beide nicht schicken soll, weiß springdoc nicht — das steht nirgends im Code.

Was hier fehlt, ist eine eigene Klasse für die Eingabe: eine, die nur title, comment und author hat. Dann stünde im Beispiel genau das, was hineingehört.

Merk dir die Stelle. Sie ist der Punkt, an dem erzeugte Dokumentation an ihre Grenze kommt — und der Grund, warum es DTOs gibt. Der Ausblick am Ende des Tutorials kommt darauf zurück.

Die Oberfläche ist nicht nur zum Lesen

Über Try it out setzt du echte Anfragen an deinen laufenden Dienst ab — direkt aus der Dokumentation. Für einen ersten Blick ist das bequemer als eine .http-Datei: Die Oberfläche kennt die Felder schon.

Was die .http-Datei weiterhin besser kann: Sie liegt im Projekt, wandert mit in die Versionsverwaltung und hält deine Testfälle fest. Die Swagger-Oberfläche ist zum Ausprobieren da, nicht zum Aufbewahren.

Diese Datei ist es, die du der Partnerfirma schickst. Aus ihr kann deren Werkzeugkette automatisch Client-Code erzeugen.

Ein Vorbehalt, den du mitschicken solltest

Das Schema beschreibt deine Entität — also die Klasse, die auch deine Datenbanktabelle abbildet. Änderst du später eine Spalte, ändert sich damit auch der Vertrag, auf den sich die Partnerfirma verlassen hat.

Deshalb trennt man in echten Projekten beides: Die Datenbank hat ihre Klasse, die Schnittstelle eine eigene. Die Frage steht im Ausblick unten.

Aufgabe 3 – Die Beschreibung verbessern

Die erzeugte Dokumentation erfasst alle Endpunkte, ist aber unpersönlich: Die Anwendung heißt „OpenAPI definition", und bei den Endpunkten steht nur, wie die Java-Methode heißt.

src/main/java/de/szut/gaestebuch/config/OpenApiConfig.java
package de.szut.gaestebuch.config;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import org.springframework.context.annotation.Configuration;

@Configuration
@OpenAPIDefinition(
info = @Info(
title = "Gästebuch-API",
version = "v1",
description = "Schnittstelle zum Anlegen, Lesen, Ändern "
+ "und Löschen von Gästebucheinträgen."))
public class OpenApiConfig {
}
@Operation(summary = "Legt einen neuen Gästebucheintrag an")
@ApiResponse(responseCode = "201", description = "Eintrag wurde angelegt")
@ApiResponse(responseCode = "400", description = "Der Rumpf war fehlerhaft")
@PostMapping
public ResponseEntity<GuestbookEntry> createEntry(...) { ... }
Was du hier ergänzt, ist wieder von Hand gepflegt

Beachte den Unterschied: Pfade, Methoden und Feldnamen liest springdoc aus dem Code — die veralten nicht. Ein @ApiResponse(responseCode = "400", …) dagegen ist eine Behauptung, die du selbst aufstellst. Ändert sich das Verhalten, zieht sie niemand automatisch nach.

Erzeugte Dokumentation verkleinert das Problem also, sie beseitigt es nicht.

Nicht jeden Endpunkt zutexten

Annotationen zur Dokumentation blähen den Controller auf. Ergänze sie dort, wo etwas nicht aus dem Code hervorgeht — etwa fachliche Bedingungen oder besondere Fehlerfälle.

Dass ein GET auf /{id} einen Eintrag liefert, sieht man auch ohne Beschreibung.

Aufgabe 4 – Testfälle

IDBeschreibungVorbedingungTestschritteErwartetes ErgebnisErgebnis
TF-01Swagger-UI ist erreichbarAnwendung läuft
  1. http://localhost:8080/swagger-ui.html aufrufen
Die Oberfläche erscheint und listet den Controller mit allen Endpunkten.
TF-02Alle Endpunkte sind aufgeführtTF-01 bestanden
  1. Die Liste durchsehen
Genau fünf Endpunkte: POST, GET (Sammlung), GET (Id), PUT, DELETE.
TF-03Try it out funktioniertTF-01 bestanden
  1. POST aufklappen
  2. Try it out klicken
  3. Gültigen Rumpf eingeben und ausführen
Status 201. Der Eintrag ist danach über GET abrufbar.
TF-04OpenAPI-Beschreibung als JSONAnwendung läuft
  1. http://localhost:8080/v3/api-docs aufrufen
Ein JSON-Objekt mit einem Feld openapi und einem Abschnitt paths.
TF-05Eigener Titel wird übernommenOpenApiConfig wurde angelegt
  1. Swagger-UI neu laden
Oben steht Gästebuch-API v1 statt „OpenAPI definition".
TF-06Die Beschreibung kennt die Felder der EntitätTF-01 bestanden
  1. Beim POST-Endpunkt den Abschnitt Request body aufklappen
  2. Schema statt Example value wählen
Alle Felder von GuestbookEntry stehen dort mit ihrem Datentyp — title, comment, author, date, id. Geschrieben hat sie niemand.
Der eigentliche Beweis kommt im nächsten Arbeitsblatt

Dass erzeugte Dokumentation nicht veralten kann, ist bis hier eine Behauptung. Im nächsten Arbeitsblatt änderst du die Schnittstelle wirklich: Der GET-Endpunkt bekommt Parameter zum Filtern und Blättern.

Sieh danach noch einmal in die Swagger-Oberfläche. Dort werden sie stehen — und du wirst dafür keine Zeile Dokumentation geschrieben haben.

Zusammenfassung

Das hast du gelernt
  • Getrennt gepflegte Dokumentation veraltet; erzeugte Dokumentation kann es nicht.
  • OpenAPI ist ein standardisiertes Beschreibungsformat für REST-Schnittstellen.
  • springdoc liest deinen Code und erzeugt daraus die Beschreibung — sichtbar in der Swagger-UI, abrufbar unter /v3/api-docs.
  • Über Try it out lassen sich Endpunkte direkt aus der Dokumentation aufrufen.
  • @OpenAPIDefinition, @Operation und @ApiResponse ergänzen, was aus dem Code nicht hervorgeht.
  • springdoc gehört nicht zu Spring Boot — deshalb muss die Version passend zur Boot-Version angegeben werden.
Ab jetzt hast du zwei Werkzeuge

requests.http liegt im Projekt und wandert mit in die Versionsverwaltung — dort stehen deine Testfälle.

Die Swagger-Oberfläche musst du dafür nicht pflegen: Sie entsteht aus dem Code. Zum schnellen Ausprobieren ist sie bequemer, weil sie die Eingabefelder selbst kennt. Im nächsten Arbeitsblatt wirst du das merken.

Selbstkontrolle

  1. Warum veraltet handgeschriebene Schnittstellendokumentation?
  2. Was ist der Unterschied zwischen OpenAPI und der Swagger-UI?
  3. Woher weiß springdoc, welche Endpunkte es gibt?
  4. Warum muss bei springdoc eine Version in der pom.xml stehen, bei spring-boot-starter-webmvc aber nicht?
  5. Wann lohnt sich @Operation an einem Endpunkt?
Antworten
  1. Weil sie eine zweite Quelle neben dem Code ist. Ändert jemand den Code, muss er daran denken, die Beschreibung nachzuziehen — und irgendwann tut das niemand.
  2. OpenAPI ist das Format, in dem die Schnittstelle beschrieben wird (JSON). Die Swagger-UI ist ein Werkzeug, das eine solche Beschreibung als bedienbare Oberfläche darstellt.
  3. springdoc untersucht beim Start die Controller-Klassen und liest deren Annotationen, Methodensignaturen und Rückgabetypen aus.
  4. Weil spring-boot-starter-webmvc im Eltern-POM von Spring Boot mit einer abgestimmten Version geführt wird. springdoc ist ein eigenständiges Projekt und steht dort nicht.
  5. Wenn etwas dokumentiert werden soll, das aus dem Code nicht hervorgeht — fachliche Bedingungen, besondere Fehlerfälle, Einschränkungen. Nicht für Selbstverständliches.