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.propertiesfile instead ofmicroprofile-config.propertiesfile - expose the OpenAPI documents on URL
/q/openapiinstead of/openapi
Links
- https://github.com/pe-st/apidocs/tree/master/design-first-quarkiverse
- https://docs.quarkiverse.io/quarkus-openapi-generator/dev/server.html
- https://github.com/quarkiverse/quarkus-openapi-generator
- https://deepwiki.com/quarkiverse/quarkus-openapi-generator/3.2-server-configuration
- https://github.com/Apicurio/apicurio-codegen
- https://myfear.substack.com/p/spec-first-or-code-first-choosing
- https://spec.openapis.org/oas/v3.0.3.html