OpenAPI Generator: Client und Server aus der Spezifikation erzeugen

OpenAPI Generator: Client und Server aus der Spezifikation erzeugen

OpenAPI Generator: Client und Server aus der Spezifikation erzeugen

Artikel-Übersicht

Der OpenAPI Generator nimmt eine Schnittstellenbeschreibung und erzeugt daraus Code — Client-Bibliotheken in gut fünfzig Sprachen, Servergerüste und Datenmodelle. Dieser Artikel beginnt mit der Verwechslung, die jedes Gespräch über das Thema überlagert: Swagger und OpenAPI sind heute zwei verschiedene Dinge, waren aber bis Version 2 dasselbe. Danach die drei Wege in die Werkzeugkette — Maven-Plugin, Kommandozeilenwerkzeug und Container-Abbild — jeweils mit vollständiger Konfiguration und der Antwort auf die überraschend häufige Frage, ob dafür eine Java-Laufzeit nötig ist. Der wichtigste Abschnitt behandelt eine Entscheidung, die in keiner Dokumentation steht und in jedem Projekt getroffen werden muss: Gehört generierter Code ins Repository? Zum Schluss das Anpassen einzelner Templates und die fünf häufigsten Fehler.

Wie man die Schnittstelle entwirft, bevor man sie generiert, steht in API-Design: Schnittstellen entwerfen, die man nicht bereut.

Swagger oder OpenAPI?

OpenAPI ist der Standard, Swagger die Werkzeugfamilie — bis Version 2 hieß der Standard selbst Swagger.

Die Geschichte in drei Sätzen: Swagger entstand 2011 als Format samt Werkzeugen und wurde 2015 von SmartBear übernommen. Im selben Jahr wurde die Spezifikation an die Linux Foundation übergeben und in OpenAPI Specification umbenannt; die Version 2.0 heißt deshalb sowohl „Swagger 2.0" als auch „OpenAPI 2.0". Ab Version 3.0 gibt es nur noch OpenAPI. Der Name Swagger blieb bei den Werkzeugen von SmartBear — Swagger UI, Swagger Editor, Swagger Codegen.

Praktisch heißt das:

  • Wenn jemand „Swagger-Datei" sagt, meint er fast immer eine OpenAPI-Spezifikation.
  • Wenn jemand „Swagger UI" sagt, meint er das Werkzeug, das eine OpenAPI-Datei als Oberfläche darstellt.
  • Swagger Codegen und OpenAPI Generator sind zwei verschiedene Projekte: Letzteres ist 2018 als Abspaltung des ersteren entstanden und wird von der Gemeinschaft weiterentwickelt.

Für neue Projekte ist der OpenAPI Generator die Empfehlung, weil er aktiver gepflegt wird und mehr Generatoren unterstützt.

Was der Generator kann

Aus einer Beschreibungsdatei erzeugt er drei Arten von Artefakten:

  • Client-Bibliotheken — eine typisierte Klasse je Endpunkt, in der Zielsprache, mit Serialisierung und Fehlerbehandlung.
  • Servergerüste — Controller-Signaturen, Datenmodelle und Routen, die man nur noch mit Logik füllt.
  • Datenmodelle — die Schemata der Spezifikation als Klassen.

Was er nicht kann: Geschäftslogik. Die Grenze verläuft dort, wo die Spezifikation aufhört — er erzeugt die Form, nicht den Inhalt.

Der eigentliche Gewinn ist dabei nicht die gesparte Tipparbeit, sondern die Garantie, dass beide Seiten dasselbe verstehen. Ein Client, der aus derselben Datei erzeugt wurde wie das Servergerüst, kann sich in Feldnamen und Typen nicht irren.

YAML oder JSON?

Beides ist erlaubt, und die Empfehlung lautet: YAML für die Pflege, JSON für die Auslieferung.

YAML erlaubt Kommentare und erzeugt lesbare Änderungsdiffs — beides ist bei einer Datei, die von mehreren Personen bearbeitet und im Review besprochen wird, entscheidend. JSON ist dafür in jeder Werkzeugkette ohne Zusatzbibliothek verarbeitbar und der bessere Auslieferungszustand.

Wer beides braucht, pflegt YAML und erzeugt JSON im Build.

Der Weg über das Maven-Plugin

Für Java-Projekte der bequemste Weg, weil die Generierung Teil des normalen Bauvorgangs wird.

<plugin>
  <groupId>org.openapitools</groupId>
  <artifactId>openapi-generator-maven-plugin</artifactId>
  <version>7.x.x</version>
  <executions>
    <execution>
      <goals><goal>generate</goal></goals>
      <configuration>
        <inputSpec>${project.basedir}/src/main/resources/api/rechnungen.yaml</inputSpec>
        <generatorName>java</generatorName>
        <library>okhttp-gson</library>
        <output>${project.build.directory}/generated-sources/openapi</output>
        <apiPackage>de.beispiel.api.client</apiPackage>
        <modelPackage>de.beispiel.api.model</modelPackage>
        <generateApiTests>false</generateApiTests>
        <generateModelTests>false</generateModelTests>
        <configOptions>
          <dateLibrary>java8</dateLibrary>
          <useJakartaEe>true</useJakartaEe>
        </configOptions>
      </configuration>
    </execution>
  </executions>
</plugin>

Die Parameter im Einzelnen:

  • inputSpec — der Pfad zur Spezifikation. Absolute Pfade über ${project.basedir} sind hier zuverlässiger als relative.
  • generatorName — welcher Generator. java für einen Client, spring für ein Servergerüst, typescript-axios für die Oberfläche.
  • library — welche HTTP-Bibliothek der erzeugte Client benutzt. Diese Wahl bindet stärker als die Sprache, weil sie im Anwendungscode sichtbar wird.
  • output — bei Generierung im Build gehört das Ziel unter target, nicht unter src.
  • generateApiTests/generateModelTests — standardmäßig an, erzeugen leere Testgerüste und blähen den Ausgabeordner auf. Fast immer abschaltbar.
  • configOptions — generatorspezifische Einstellungen, hier etwa die Datumsbibliothek.

Das Plugin hängt sich standardmäßig in die Phase generate-sources, läuft also vor dem Übersetzen. Ein mvn clean compile genügt zum Prüfen.

Der Weg über die Kommandozeile

Drei Varianten, alle gleichwertig im Ergebnis.

# npm-Paket
npx @openapitools/openapi-generator-cli generate \
  -i rechnungen.yaml -g typescript-axios -o ./generated/api

# Container
docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli generate \
  -i /local/rechnungen.yaml -g python -o /local/generated

# JAR direkt
java -jar openapi-generator-cli.jar generate \
  -i rechnungen.yaml -g go -o ./generated

Und hier die Antwort auf die Frage, die überraschend oft gestellt wird: Ja, der Generator braucht eine Java-Laufzeit — auch das npm-Paket. Das Paket ist ein schlanker Aufsatz, der das eigentliche JAR herunterlädt und über eine JVM startet. Wer kein Java auf dem Rechner haben will, nimmt das Container-Abbild; dort ist die Laufzeit enthalten.

Der Container-Weg hat einen zweiten Vorteil: Er friert die Generator-Version über das Abbild-Tag ein. Das ist mehr wert, als es klingt — siehe unten.

Generierten Code einchecken oder nicht?

Die Entscheidung, die jedes Projekt trifft und über die keine Dokumentation spricht. Beide Wege sind vertretbar, sie haben nur verschiedene Folgen.

Einchecken:

  • Änderungen an der Schnittstelle werden im Review sichtbar — man sieht am Diff, was sich für die Aufrufer ändert. Das ist der stärkste Vorteil.
  • Der Build braucht den Generator nicht, also kein Netzwerkzugriff und keine JVM in der Bauumgebung.
  • Neue Teammitglieder können übersetzen, ohne die Werkzeugkette einzurichten.
  • Das Risiko: Jemand ändert generierten Code von Hand. Beim nächsten Lauf ist die Änderung weg — oder schlimmer, sie bleibt, weil niemand mehr neu generiert.

Im Build erzeugen:

  • Es kann nichts auseinanderlaufen, weil es nur eine Quelle gibt.
  • Kein generierter Code in der Versionshistorie, also kleinere Diffs.
  • Das Risiko: Der Build hängt am Generator, an dessen Version und an Netzwerkzugriff. Ein Versionssprung ändert die Ausgabe, ohne dass jemand etwas an der Spezifikation geändert hat.

Und der dritte Weg, den ich für mehrere Aufrufer empfehle: Den Client als eigenes, versioniertes Artefakt erzeugen und veröffentlichen. Ein kleines Projekt enthält nur die Spezifikation und die Generatorkonfiguration, baut daraus eine Bibliothek und legt sie in die Paketverwaltung. Die Aufrufer binden eine Version ein wie jede andere Abhängigkeit. Damit ist die Frage „einchecken oder nicht" hinfällig, und die Versionierung der Schnittstelle wird explizit.

Wenn du einchecken willst, dann mit zwei Vorkehrungen: Der Ausgabeordner bekommt eine deutliche Kennzeichnung als generiert, und ein Prüfschritt im Bauvorgang generiert neu und schlägt fehl, wenn sich etwas unterscheidet. Damit ist Handbearbeitung ausgeschlossen.

Templates anpassen

Manchmal passt die Ausgabe nicht — andere Namenskonventionen, eigene Fehlerbehandlung, zusätzliche Annotationen. Der Generator arbeitet mit Vorlagen, die sich einzeln überschreiben lassen.

Der Weg, der sich bewährt hat:

# Die Vorlagen des gewählten Generators ausgeben
npx @openapitools/openapi-generator-cli author template -g java --library okhttp-gson

# Nur die geänderte Datei behalten, Rest löschen, dann:
npx @openapitools/openapi-generator-cli generate \
  -i rechnungen.yaml -g java -t ./templates -o ./generated

Wichtig ist, nur die tatsächlich geänderten Vorlagen zu behalten. Wer den ganzen Ordner kopiert, friert damit auch alle unveränderten Vorlagen ein — und verliert bei jedem Generator-Update sämtliche Verbesserungen und Fehlerkorrekturen. Der Generator nimmt für alles, was nicht im eigenen Ordner liegt, automatisch seine mitgelieferte Fassung.

Wenn es nicht läuft

1. Die Spezifikation ist ungültig. Der Generator meldet das nicht immer deutlich. Prüfschritt: Die Datei zuerst mit validate prüfen, bevor man generiert.

2. Keine Java-Laufzeit vorhanden. Betrifft auch das npm-Paket. Prüfschritt: java -version ausführen.

3. Namenskollisionen im erzeugten Code. Zwei Schemata, die sich nur in Groß- und Kleinschreibung unterscheiden, oder ein Feld, das in der Zielsprache ein Schlüsselwort ist. Prüfschritt: Die Namen in der Spezifikation bereinigen — das ist die richtige Stelle, nicht der erzeugte Code.

4. Ein Versionssprung hat die Ausgabe geändert. Der unangenehmste Fall, weil er auftritt, ohne dass jemand etwas geändert hat. Prüfschritt: Generator-Version fest angeben und nie mit einem beweglichen Tag arbeiten.

5. Das Servergerüst passt nicht zum Rahmenwerk. Die Generatoren für Servergerüste sind eng an bestimmte Versionen gebunden. Prüfschritt: Die passende Generator- und Bibliotheksfassung wählen und beide Versionen dokumentieren. Für Java-Server ist das meist die Spring-Variante — der Einstieg dazu steht in Spring Boot: Einstieg für Java-Entwickler.

Die Alternativen

Werkzeug Stärke Schwäche
OpenAPI Generator über 50 Sprachen, aktive Gemeinschaft, anpassbare Vorlagen Ausgabe ändert sich zwischen Hauptversionen
Swagger Codegen das ursprüngliche Projekt, kommerzieller Rückhalt weniger Generatoren, langsamere Entwicklung
Sprachspezifische Werkzeuge oft idiomatischere Ausgabe nur eine Sprache, kleinere Gemeinschaft

Für die meisten Projekte ist der OpenAPI Generator die richtige Wahl. Wer nur eine Zielsprache braucht und mit der erzeugten Form unzufrieden ist, sollte einmal prüfen, ob es für diese Sprache ein spezialisiertes Werkzeug gibt — die Ausgabe liest sich dort häufig natürlicher.

Häufige Fragen

Was ist der OpenAPI Generator? Ein Werkzeug, das aus einer OpenAPI-Spezifikation Client-Bibliotheken, Servergerüste und Datenmodelle in über fünfzig Sprachen erzeugt.

Ist OpenAPI dasselbe wie Swagger? Nicht mehr. Bis Version 2 hieß der Standard Swagger, seit der Übergabe an die Linux Foundation heißt er OpenAPI. Swagger ist heute der Name einer Werkzeugfamilie.

Braucht der OpenAPI Generator Java? Ja, auch das npm-Paket — es lädt das eigentliche JAR nach und startet es über eine JVM. Wer ohne Java auskommen will, nimmt das Container-Abbild.

YAML oder JSON für die Spezifikation? YAML für die Pflege wegen Kommentaren und lesbaren Diffs, JSON für die Auslieferung.

Wird OpenAPI noch verwendet? Ja, es ist der De-facto-Standard für die Beschreibung von HTTP-Schnittstellen und Grundlage für Dokumentation, Codegenerierung und Testwerkzeuge.

Gehört generierter Code ins Repository? Beides ist vertretbar. Bei mehreren Aufrufern ist ein eigenes, versioniertes Client-Artefakt der sauberste Weg.

Zusammenfassung und praktische Tipps

Der Generator nimmt Arbeit ab und bringt eigene Entscheidungen mit. Die wichtigsten Punkte:

  • Swagger ist die Werkzeugfamilie, OpenAPI der Standard — bis Version 2 dasselbe, seitdem nicht mehr.
  • Alle drei Wege brauchen eine Java-Laufzeit, auch das npm-Paket. Der Container ist der Ausweg.
  • Generator-Version fest angeben. Ein Sprung ändert die Ausgabe, ohne dass jemand die Spezifikation angefasst hat.
  • Nur geänderte Vorlagen behalten, sonst friert man den ganzen Generator ein.
  • Für mehrere Aufrufer ein eigenes Client-Artefakt bauen statt einzuchecken oder im Build zu erzeugen.
  • Namenskollisionen in der Spezifikation beheben, nicht im erzeugten Code.

Mein Rat für den Einstieg: Fang mit dem Container-Weg an, auch wenn du ein Java-Projekt hast. Er zwingt dich, die Generator-Version festzulegen, er funktioniert auf jedem Rechner gleich, und du siehst in fünf Minuten, ob die erzeugte Ausgabe überhaupt zu deinem Projekt passt. Die Einbindung in den Bauvorgang lohnt sich erst, wenn diese Frage beantwortet ist.

Ressourcen