OpenAPI, Java en Typescript

Open API specification is een gestandaardiseerde manier om REST interfaces te beschrijven. Deze specificatie vind zijn oorsprong in het in 2010 gestarte project Swagger. In 2016 is dit project ondergebracht bij de Open API specification initiative. In veel code en tools zul je dus nog regelmatig verwijzingen naar Swagger tegen komen. Inmiddels is versie 3.0.0 van de specificatie uit.

De specificatie die je definieert kun je vergelijken met een WSDL definitie voor een SOAP service. Er kan in gedefinieerd worden welke methode beschikbaar zijn, welke parameters met type er in gaan en wat er terug komt. Een grafische editor voor de definitie kun je vinden op de swagger site.



Een andere optie is om op basis van bijvoorbeeld Java code een definitie te genereren. Hiervoor kunnen de gebruikelijke Spring MVC annotaties gebruikt worden zoals: @Controller , @RequestMapping , @PathVariable , @RequestBody  en @RequestParam . Daarnaast zijn er nog extra annotaties te vinden in bijvoorbeeld de swagger annotation library.

Bij het maken van de keuze tussen het genereren of vooraf definiëren van de specificatie moet over het volgende nagedacht worden. Bij het genereren op basis van code is het noodzakelijk om een extra stap op te nemen in je build proces zodat de definitie gegenereerd wordt. Ook kan het zijn dat er extra annotaties nodig zijn om alles vast te leggen en daarmee ‘vervuil’ je je productie code. Andersom kan er gebruik gemaakt worden van de standaard generate-sources stap.

De definitie genereren tijdens het build process kan door middel van een unit test. In deze test initialiseer je het spring MVC framework met alle controllers. Daarna is het mogelijk om met behulp van de volgende code een specificatie te genereren.

String outputDir = System.getProperty("io.springfox.staticdocs.outputDir");
MvcResult mvcResult = this.mockMvc.perform( get( "/v2/api-docs")
                                  .accept( MediaType.APPLICATION_JSON))
                                  .andExpect(status().isOk())
                                  .andReturn();

MockHttpServletResponse response = mvcResult.getResponse();
String swaggerJson = response.getContentAsString();
Files.createDirectories( Paths.get( outputDir));
try (BufferedWriter writer = Files.newBufferedWriter( Paths.get( outputDir, "swagger.json"), StandardCharsets.UTF_8)){
    writer.write(swaggerJson);
}

Deze definitie kan opgepakt worden door de maven ‘openapi-generator-maven-plugin’ om een client te genereren in een taal en framework naar keuze. Een lijst met ondersteunde mogelijkheden vind je hier: https://github.com/OpenAPITools/openapi-generator.

Het onderstaande voorbeeld is voor een Angular 7 project:

<plugin>
  <groupId>org.openapitools</groupId>
  <artifactId>openapi-generator-maven-plugin</artifactId>
  <version>3.3.4</version>
  <executions>
    <execution>
      <phase>prepare-package</phase>
      <goals>
        <goal>generate</goal>
      </goals>
      <configuration>
        <inputSpec>${project.build.directory}/swagger.json</inputSpec>
        <language>typescript-angular</language>
        <configOptions>
          <ngVersion>7.0</ngVersion>
        </configOptions>
        <output>${project.build.directory}/generated-client</output>
      </configuration>
    </execution>
  </executions>
</plugin>

Dezelfde plugin kan ook gebruikt worden voor het genereren van van de server stub om zo tot bijvoorbeeld een Spring implementatie te komen.

Het gebruik van de OpenAPI specificatie in het build process voorkomt het maken van fouten in aanroepen tussen client en server. De compiler zal veel fouten al van te voren detecteren, mits er gebruik gemaakt wordt van een type checking. Tevens is er al een compleet model van DTOs wat het schrijven van code reduceert.