MicroProfile OpenAPI—Design First with Quarkus

This article is the third installment of my MicroProfile OpenAPI series, the former two were

The second article covered the Design First approach of working with OpenAPI documents, generating (server) code with either openapi-generator or swagger-codegen. But these two code generators are not the only possibilities, especially when using Quarkus as my examples do.

Enter the third contender for the Design First approach: Quarkus OpenAPI Generator

Quarkus OpenAPI Generator

The Quarkiverse Docs page talks about two types of generators:

  • Quarkus - OpenAPI Generator - Client generates code to call a service with an OpenAPI document from a Quarkus application (repackaging of openapi-generator as Quarkus extension)
  • Quarkus - OpenAPI Generator - Server generates code to implement a server for an given interface specification (an OpenAPI document), based on the Apicurio codegen tool

As my articles showcase examples to create servers I’ll focus only on the server generator; it’s also more interesting as the Quarkus - OpenAPI Generator - Client uses openapi-generator under the hood, which was covered as server generator already in my second article. The Quarkus - OpenAPI Generator - Server on the other hand uses the Apicurio codegen, which underneath builds on the jsonschema2pojo library.

Configuration

In stark contrast to the dozens of configuration options of openapi-generator or swagger-codegen there are only six options for the Quarkus OpenAPI Server Generator, of which four are used in my example:

quarkus.openapi.generator.spec=openapi.yaml
quarkus.openapi.generator.input-base-dir=src/main/resources/META-INF
quarkus.openapi.generator.base-package=ch.schlau.pesche.apidocs.quarkiverse.designfirst.generated.model
quarkus.openapi.generator.use-bean-validation=true

Also writing a pom.xml for a project using the Quarkus OpenAPI Server Generator is simpler than either of the other two generators, which both have a 30+ line maven plugin section.

The Quarkus OpenAPI Server Generator just needs one dependency…

<dependency>
    <groupId>io.quarkiverse.openapi.generator</groupId>
    <artifactId>quarkus-openapi-generator-server</artifactId>
    <version>${quarkus-openapi-generator.version}</version>
</dependency>

…and two additional lines for the quarkus plugin: the <extensions>true</extensions> line and the <goal>generate-code</goal> line. Here’s the complete quarkus plugin section from the pom.xml (see the source code):

<plugin>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-maven-plugin</artifactId>
    <version>${quarkus.version}</version>
    <extensions>true</extensions>
    <executions>
        <execution>
            <goals>
                <goal>build</goal>
                <goal>generate-code</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Note that in the documentation page there is also a <goal>generate-code-tests</goal> line, but I’m not sure it’s really helpful. At least in my examples the generated tests are useless.

OpenAPI specification extensions

Some more customizations of the generated code beyond the small set of options in the application.properties file are possible through the use of extensions (as documented in the OpenAPI specification), a set of proprietary property fields (all prefixed with x-codegen) to be used directly in the OpenAPI documents, see codegen-extensions.

Drawbacks

Using existing classes instead of generated ones (import mapping or schema mapping, see also Import Mapping Gotcha) is not possible with Quarkus OpenAPI Server Generator, e.g. the mapping from the schema Pan to an existing class Pan.java as shown in the design-first-openapi-generator example (using <schemaMapping>Pan=(package).Pan</schemaMapping>) can’t currently be done, there are no configuration options that would allow this.

Another gotcha is the generated interface ApiResource. This interface contains the endpoint methods that your server application should implement. When I first ran the generator it created this code

@Path("/api")
public interface ApiResource {
  @Operation(summary = "Check the pin", operationId = "pinCheck")
  @Path("/txproc/pincheck")
  @POST
  @Consumes("application/json")
  void pinCheck(@Valid @NotNull PinCheckRequest data);

  @Operation(summary = "Authorize a Purchase", operationId = "purchase")
  @Path("/txproc/purchase")
  @POST
  @Consumes("application/json")
  void purchase(@Valid @NotNull PurchaseAuthRequest data);
}

Unfortunately though the two methods in this interface should not return void, but the reponses schemas PinCheckResponse and PurchaseAuthResponse from the OpenAPI document, I would have expected this interface:

@Path("/api")
public interface ApiResource {
  @Operation(summary = "Check the pin", operationId = "pinCheck")
  @Path("/txproc/pincheck")
  @POST
  @Consumes("application/json")
  PinCheckResponse pinCheck(@Valid @NotNull PinCheckRequest data);

  @Operation(summary = "Authorize a Purchase", operationId = "purchase")
  @Path("/txproc/purchase")
  @POST
  @Consumes("application/json")
  PurchaseAuthResponse purchase(@Valid @NotNull PurchaseAuthRequest data);
}

After trying this and that and comparing with the example in the Apicurio repository I realized that I had to slightly change my openapi.yaml input file and use 200 instead of default in the Responses Object:

/api/txproc/purchase:
  post:
    # ...
    responses:
      # using "default:" here instead of "200:" generates a wrong ApiResource class
      200:
        description: Purchase Response
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PurchaseAuthResponse'

Is it mature yet?

I’m not sure if the Quarkus OpenAPI Server Generator is ready for prime time yet (see the gotchas above); the documentation says …since this extension has not been yet released… and is using a SNAPSHOT version in the given code example (<version>3.0.0-SNAPSHOT</version>); but the extension IS released (together with the client generator).

It is hard to find any web pages talking about the OpenAPI generator Quarkus extension (except the Extension Quarkiverse Page and some issue reports). There is one article mentioning it (Choosing Your OpenAPI Strategy with Quarkus vs. Spring), but there are small details in it (e.g. it is using the maven groupId io.quarkus instead of io.quarkiverse.openapi.generator) that let me wonder if the code in the article really works (there is no repository linked that would allow easy testing). If there are no working examples out there on the web (well NOW there is), does the extension then exist at all?

Conclusion

The Quarkus OpenAPI Generator provides a very nice integration of an OpenAPI code generator for Quarkus applications. There are IMHO a couple of maturity issues with the server generator, thus for real-world applications I tend to prefer the Quarkus - OpenAPI Generator - Client (using the same integration into Quarkus as the server generator, but using the mature openapi-generator as underlying library).

The drawback with the client generator is that you have to implement the server resources yourself, without a nice ApiResource interface, but the request and response classes differ usually only in details between the different generators, see the second installment of the article series for a discussion of the openapi-generator library itself (outside of the Quarkus extension).

Quarkus vs MicroProfile

Please be aware that the code in this article series is not the most idiomatic Quarkus code; as Quarkus is just the MicroProfile implementation I chose to use to demonstrate the MicroProfile OpenAPI integration with existing OpenAPI libraries.

A real-world Quarkus application would probably use

  • an application.properties file instead of microprofile-config.properties file
  • expose the OpenAPI documents on URL /q/openapi instead of /openapi

Peter Steiner

Software Developer and Opinionated Citizen

Switzerland