API-First-Lifecycle in einem Spring-Boot-Projekt mit Maven
Seite 2: Spring Boot mit API First
Nun hat man einen Spring-Boot-Stub, der alle in openapi.yaml definierten API Endpunkte enthält. mvn spring-boot:run
startet die Anwendung. Springdoc erstellt automatisch das zugehörige OAS3-Dokument und stellt es unter http://localhost:8080/swagger-ui/index.html als Swagger UI zur Verfügung.
Damit hat man jedoch API First noch nicht integriert. Ist die Additional Property skipDefaultInterface=false
gesetzt, antworten die APIs bereits mit den eingetragenen Beispielwerten aus openapi.yaml. Daher ist auch das Verwenden des Felds example
in openapi.yaml immer zu empfehlen. Auch andere Tools wie Postman verwerten die Beispielwerte. Entwicklerinnen und Entwickler können nun die API z.B. mit Postman testen und bekommen den HttpStatus.NOT_IMPLEMENTED
zurĂĽck (= 501).
Der Statuscode 501 ist sinnvoll, da dies dem aktuellen Entwicklungsstand entspricht. Der Controller muss nicht sofort alle Methodensignaturen vom Interface ĂĽberschreiben. Das Beantworten von Requests ĂĽbernimmt, solange die Klasse ApiUtil.java
(die dann später überflüssig wird) mit statischen Beispielwerten aus der openapi.yaml. Erst wenn die Methoden im Controller implementiert wurden, setzt man damit den Statuscode auf 200 bzw. 201.
Nun kommt die Kontinuität ins Spiel: Denn haben Entwicklerinnen und Entwickler einmal die Parameter in openapi.yaml definiert und angefangen die eigentliche Anwendung zu entwickeln, ändern und erweitern sich die Anforderungen an die API in Zyklen. Dabei wäre es unpraktisch, mit dem Generieren von vorn zu beginnen und die bereits implementierte Logik im Controller wieder zu überschreiben. Auf der anderen Seite soll openapi.yaml dem tatsächlichen Stand der Anwendung entsprechen und Single Source of Truth bleiben. Das kann man mit einer kontinuierlichen Integration in zwei Schritten erreichen:
Als Erstes kopiert man openapi.yaml in das resources-Verzeichnis von Spring Boot, sodass sie im Classpath enthalten ist und dem Maven Plug-in zur VerfĂĽgung steht. Zum Zweiten verwendet der OpenAPI-Generator das Maven Plug-in. Durch den Parameter apiFirst=true
in den additional-properties
ist dieses bereits beim Stub in pom.xml vorhanden und muss nur noch angepasst werden. Im Plug-in findet die eigentliche Integration von API First statt.
Ă„hnlich wie beim Stub gibt es Additional Properties, die in pom.xml <configOptions>
heiĂźen:
- In
<inputSpec>
sollte der Pfad zu der zu verwendenden openapi.yaml bereits korrekt angegeben sein. - Der Parameter
skipDefaultInterface
ist für die erste Entwicklungsphase nützlich, da sonst der Code wegen der fehlenden Implementierung der Interfaces nicht kompilierbar wäre. Später lässt sich das auf<skipDefaultInterface>true</skipDefaultInterface>
umstellen. Das ist deshalb zu empfehlen, weil die Default Interfaces die KlasseApiUtil.java
benötigen, die von manchen statischen Sourcecode-Scannern als Sicherheitslücke angesehen werden. Nach der Umstellung sollten Anwenderinnen und Anwender diese Klasse löschen. - Die Paketnamen für die Models und Interfaces definieren die Parameter
<apiPackage>
und<modelPackage>
. - Ist Spring Boot 3 gewĂĽnscht, gibt es den Parameter
<useSpringBoot3>
, andernfalls bleibt es bei Version 2.7.x. - Wichtig ist noch
<interfaceOnly>true</interfaceOnly>
, damit die nun manuell gepflegten Controller-Klassen in der Anwendung nicht mehr generiert werden. <generateSupportingFiles>false</generateSupportingFiles>
verhindert das Generieren von Controllern, pom.xml und ähnlichem.
In der IDE mĂĽssen Entwicklerinnen und Entwickler das entsprechende Verzeichnis noch zum Classpath hinzufĂĽgen, oder in IntelliJ den Pfad target/generated-sources/openapi/src/main/java
als Generated Sources Root
hinzufĂĽgen.
Lebenslauf einer API-First-Anwendung mit Maven
Ein Anpassungszyklus der API-First-Anwendung beginnt beim OpenAPI und der Datei openapi.yaml. Dann erfolgt der Build mit mvn clean compile
oder auch mit mvn clean generate-sources
, wobei Letzteres im mvn compile
enthalten ist. Erst jetzt passen die Entwicklerinnen und Entwickler die Controller-Klassen und die Businesslogik an.
Der Nachteil bei API First ist, dass der automatisch generierte Code manchmal nicht den eigenen Bedürfnissen entspricht. Es gibt zwar Konfigurationsmöglichkeiten, jedoch möchten Programmiererinnen und Programmierer beispielsweise oft dem Java-POJO Methoden hinzufügen oder Getter/Setter weglassen. Außer einzelne Dateien vom Generieren auszunehmen, bietet der OpenAPI Generator weitere Möglichkeiten, Code anzupassen. So lassen sich Mustache Templates herausziehen, oder sogar eigene Generatoren schreiben. Diese Themen gehen jedoch über den Rahmen dieses Artikels hinaus.
Zusätzlich einen Client zu generieren ist nicht weiter schwierig. Im Maven-Plug-in erstellt man dafür ein weiteres Tag <execution>
mit einer eindeutigen ID. Im Plug-in wird unter <executions> eine weitere <execution> hinzugefĂĽgt. Zum Beispiel <execution><id>sampleclient</id></execution>
. Nun folgt die komplette weitere Konfiguration, je nach Client sowie gegebenenfalls in <inputSpec>
ein weiteres OpenAPI-Dokument.