MicroProfile OpenAPI for everyone
MicroProfile OpenAPI is primarily used for adding OpenAPI to JAX-RS Endpoints. In this blog post we will look at how the SmallRye Implementation extends this with some extra features, and support for more web frameworks, when used in Quarkus.
Using Quarkus
The example code is available here. You can also initialize a project using code.quarkus.io – just make sure to include the SmallRye OpenAPI Extension.
JAX-RS
Let’s start with a basic JAX-RS Example in Quarkus. We have a Greeting
Object, that has a message
and a to
field, and we will create GET
, POST
and DELETE
endpoints for the greeting.
Apart from the usual Quarkus setup, you also need the following in your pom.xml
to create a JAX-RS Endpoint:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-jsonb</artifactId> </dependency> |
In Quarkus you do not need an Application
class, we can just add the Endpoint class:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @Path ( "/jax-rs" ) @Produces (MediaType.APPLICATION_JSON) @Consumes (MediaType.APPLICATION_JSON) public class JaxRsGreeting { @GET @Path ( "/hello" ) public Greeting helloJaxRs() { return new Greeting( "Hello" , "JAX-RS" ); } @POST @Path ( "/hello" ) public Greeting newHelloJaxRs(Greeting greeting) { return greeting; } @DELETE @Path ( "/hello/{message}" ) public void deleteHelloJaxRs( @PathParam ( "message" ) String message) { // Here do the delete. } } |
So far we have not yet added any MicroProfile OpenAPI Annotations, but because we added the quarkus-smallrye-openapi
extension, we will already have a Schema document generated under /openapi
:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | --- openapi: 3.0 . 3 info: title: Generated API version: "1.0" paths: /jax-rs/hello: get: responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: requestBody: content: application/json: schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /jax-rs/hello/{message}: delete: parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content components: schemas: Greeting: type: object properties: message: type: string to: type: string |
See quarkus.io/guides/rest-json for more information.
OpenAPI
You can add more information to the generated schema document by using MicroProfile OpenAPI.
Header information using config
One feature that we added to SmallRye is the ability to add header information, that you typically add to the Application
class using annotations, with MicroProfile config. This is useful in Quarkus where you do not need an Application
class. So adding the following to the application.properties
will give you some header information:
01 02 03 04 05 06 07 08 09 10 | mp.openapi.extensions.smallrye.info.title=OpenAPI for Everyone %dev.mp.openapi.extensions.smallrye.info.title=OpenAPI for Everyone (development) %test.mp.openapi.extensions.smallrye.info.title=OpenAPI for Everyone (test) mp.openapi.extensions.smallrye.info.version= 1.0 . 0 mp.openapi.extensions.smallrye.info.description=Example on how to use OpenAPI everywhere mp.openapi.extensions.smallrye.info.contact.email=phillip.kruger @redhat .com mp.openapi.extensions.smallrye.info.contact.name=Phillip Kruger mp.openapi.extensions.smallrye.info.contact.url=https: //www.phillip-kruger.com mp.openapi.extensions.smallrye.info.license.name=Apache 2.0 mp.openapi.extensions.smallrye.info.license.url=http: //www.apache.org/licenses/LICENSE-2.0.html |
Now look at the header of the generated schema document under /openapi
:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | --- openapi: 3.0 . 3 info: title: OpenAPI for Everyone (development) description: Example on how to use OpenAPI everywhere contact: name: Phillip Kruger url: https: //www.phillip-kruger.com email: phillip.kruger @redhat .com license: name: Apache 2.0 url: http: //www.apache.org/licenses/LICENSE-2.0.html version: 1.0 . 0 # rest of the schema document ... |
Adding some OpenAPI Annotations to your operations
You can use any of the annotations in MicroProfile OpenAPI to further describe your endpoint, for example the Tag
annotation:
1 2 3 4 5 6 7 | @Path ( "/jax-rs" ) @Produces (MediaType.APPLICATION_JSON) @Consumes (MediaType.APPLICATION_JSON) @Tag (name = "JAX-RS Resource" , description = "Basic Hello World using JAX-RS" ) public class JaxRsGreeting { \\... } |
Auto generate the operation id
Some tools that use the schema document to generate client stubs, need an operationId
in the schema document that is used to name the client stub methods. We added support in SmallRye to auto generate this using either the method name (METHOD
), class and method name (CLASS_METHOD
), or package, class and method name (PACKAGE_CLASS_METHOD
). To do this add the following to application.properties
:
1 | mp.openapi.extensions.smallrye.operationIdStrategy=METHOD |
You will now see the operationId
in the schema document for every operation:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | --- openapi: 3.0 . 3 # header omitted ... /jax-rs/hello: get: tags: - JAX-RS Resource operationId: helloJaxRs responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: tags: - JAX-RS Resource operationId: newHelloJaxRs requestBody: content: application/json: schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /jax-rs/hello/{message}: delete: tags: - JAX-RS Resource operationId: deleteHelloJaxRs parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content |
Changing the OpenAPI version
Some API gateways might require a certain OpenAPI version to work. The schema document generated by the SmallRye extension will generate with a 3.0.3
as the version, but since there is only minor differences between these versions, you can change that to 3.0.0
, 3.0.1
or 3.0.2
. You can do this by adding this in application.properties
:
1 | mp.openapi.extensions.smallrye.openapi= 3.0 . 2 |
Now the version generated will be:
1 2 3 4 | --- openapi: 3.0 . 2 # Rest of the document ... |
See quarkus.io/guides/openapi-swaggerui for more information.
Spring Web
Recently support for Spring Web has been added in SmallRye, this means that, not only will you see the default OpenAPI document when you use Spring Web in Quarkus, but you can also use MicroProfile OpenAPI to further describe your Spring Web endpoints.
Let’s add a Spring Rest Controller to our current application. First add this in your pom.xml
:
1 2 3 4 | <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-spring-web</artifactId> </dependency> |
Now you can create a similar endpoint to the JAX-RS one we have looked at so far, but using Spring Web:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | @RestController @RequestMapping (value = "/spring" , produces = MediaType.APPLICATION_JSON_VALUE) @Tag (name = "Spring Resource" , description = "Basic Hello World using Spring" ) public class SpringGreeting { @GetMapping ( "/hello" ) public Greeting helloSpring() { return new Greeting( "Hello" , "Spring" ); } @PostMapping ( "/hello" ) public Greeting newHelloSpring( @RequestBody Greeting greeting) { return greeting; } @DeleteMapping ( "/hello/{message}" ) public void deleteHelloSpring( @PathVariable (name = "message" ) String message) { } } |
The Spring annotations will be scanned and this will be added to your schema document:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | --- openapi: 3.0 . 3 # header omitted ... /spring/hello: get: tags: - Spring Resource operationId: helloSpring responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: tags: - Spring Resource operationId: newHelloSpring requestBody: content: '*/*' : schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /spring/hello/{message}: delete: tags: - Spring Resource operationId: deleteHelloSpring parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content |
See quarkus.io/guides/spring-web for more information.
Vert.x Reactive Routes
In Quarkus, you can also build Vert.x endpoints using Reactive Routes. Similarly to Spring Web, your endpoints will be available in the OpenAPI Schema and can be further described using MicroProfile OpenAPI. To add a Vert.x reactive Route in Quarkus, you need the following in your pom.xml
:
1 2 3 4 | <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-vertx-web</artifactId> </dependency> |
Now you can create the endpoint:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | @ApplicationScoped @RouteBase (path = "/vertx" , produces = "application/json" ) @Tag (name = "Vert.x Resource" , description = "Basic Hello World using Vert.x" ) public class VertxGreeting { @Route (path = "/hello" , methods = HttpMethod.GET) public Greeting helloVertX() { return new Greeting( "Hello" , "Vert.x" ); } @Route (path = "/hello" , methods = HttpMethod.POST) public Greeting newHelloVertX( @Body Greeting greeting) { return greeting; } @Route (path = "/hello/:message" , methods = HttpMethod.DELETE) public void deleteHelloVertX( @Param ( "message" ) String message) { } } |
and now your Vert.x Routes are available in OpenAPI:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | --- openapi: 3.0 . 3 # header omitted ... /vertx/hello: get: tags: - Vert.x Resource operationId: helloVertX responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: tags: - Vert.x Resource operationId: newHelloVertX requestBody: content: '*/*' : schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /vertx/hello/{message}: delete: tags: - Vert.x Resource operationId: deleteHelloVertX parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content |
See quarkus.io/guides/reactive-routes for more information.
Endpoints generated with Panache
In Quarkus your can generate your JAX-RS endpoint using Panache. These generated classes will also be scanned and added to the OpenAPI schema document if you have the quarkus-smallrye-openapi
extension in your pom.xml
.
See quarkus.io/guides/rest-data-panache for more information.
Any other Web Framework
You can also add any other endpoint to your document by providing that part of the Schema document in a yaml
file. Let’s say for instance you have a Servlet that exposes some methods and you want to add those to the schema document. Servlet is just an example, any web framework can work here.
So first we add this to the pom.xml
to add Servlet support in Quarkus:
1 2 3 4 | <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-undertow</artifactId> </dependency> |
We can now create a Servlet Endpoint like this for example:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | @WebServlet ( "/other/hello/*" ) public class ServletGreeting extends HttpServlet { private static final Jsonb JSONB = JsonbBuilder.create(); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType( "application/json" ); Greeting greeting = new Greeting( "Hello" , "Other" ); PrintWriter out = response.getWriter(); out.print(JSONB.toJson(greeting)); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType( "application/json" ); Greeting greeting = JSONB.fromJson(request.getInputStream(), Greeting. class ); PrintWriter out = response.getWriter(); out.print(JSONB.toJson(greeting)); } @Override protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Here do the delete } } |
Now we need a OpenAPI Schema document that maps to these endpoints. You need to add this to a file called openapi.yml
in src/main/resources/META-INF
:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | --- openapi: 3.0 . 3 tags: - name: Other Resource description: Basic Hello World using Something else paths: /other/hello: get: tags: - Other Resource operationId: helloOther responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: tags: - Other Resource operationId: newHelloOther requestBody: content: application/json: schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /other/hello/{message}: delete: tags: - Other Resource operationId: deleteHelloOther parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content |
This will be merged with the rest of the endpoints to expose all paths in your document. So in the end your /openapi
output will look like this:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | --- openapi: 3.0 . 2 info: title: OpenAPI for Everyone (development) description: Example on how to use OpenAPI everywhere contact: name: Phillip Kruger url: https: //www.phillip-kruger.com email: phillip.kruger @redhat .com license: name: Apache 2.0 url: http: //www.apache.org/licenses/LICENSE-2.0.html version: 1.0 . 0 tags: - name: Other Resource description: Basic Hello World using Something else - name: Spring Resource description: Basic Hello World using Spring - name: JAX-RS Resource description: Basic Hello World using JAX-RS - name: Vert.x Resource description: Basic Hello World using Vert.x paths: /other/hello: get: tags: - Other Resource operationId: helloOther responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: tags: - Other Resource operationId: newHelloOther requestBody: content: application/json: schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /other/hello/{message}: delete: tags: - Other Resource operationId: deleteHelloOther parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content /jax-rs/hello: get: tags: - JAX-RS Resource operationId: helloJaxRs responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: tags: - JAX-RS Resource operationId: newHelloJaxRs requestBody: content: application/json: schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /jax-rs/hello/{message}: delete: tags: - JAX-RS Resource operationId: deleteHelloJaxRs parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content /spring/hello: get: tags: - Spring Resource operationId: helloSpring responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: tags: - Spring Resource operationId: newHelloSpring requestBody: content: '*/*' : schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /spring/hello/{message}: delete: tags: - Spring Resource operationId: deleteHelloSpring parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content /vertx/hello: get: tags: - Vert.x Resource operationId: helloVertX responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' post: tags: - Vert.x Resource operationId: newHelloVertX requestBody: content: '*/*' : schema: $ref: '#/components/schemas/Greeting' responses: "200" : description: OK content: application/json: schema: $ref: '#/components/schemas/Greeting' /vertx/hello/{message}: delete: tags: - Vert.x Resource operationId: deleteHelloVertX parameters: - name: message in: path required: true schema: type: string responses: "204" : description: No Content components: schemas: Greeting: type: object properties: message: type: string to: type: string |
This contains resources from JAX-RS, Spring Web, Vert.x Reactive Routes and Servlet.
Swagger UI
In Quarkus, Swagger-UI is included by default and when you now browse to localhost:8080/swagger-ui you will see the UI with all your endpoints:
Summary
In this post we looked at how Quarkus extends the MicroProfile OpenAPI specification to make it even easier to document your Endpoints. We also looked at how you can document any web framework using it.
If you find any issues or have any suggestions, head over to the SmallRye project and let’s discuss it there.
Published on Java Code Geeks with permission by Phillip Krüger, partner at our JCG program. See the original article here: MicroProfile OpenAPI for everyone Opinions expressed by Java Code Geeks contributors are their own. |