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:
<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:
@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
:
---
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:
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
:
---
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:
@Path("/jax-rs")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "JAX-RS Resource", description = "Basic Hello World using JAX-RS") (1)
public class JaxRsGreeting {
//...
}
1 | Example usage of MicroProfile OpenAPI annotation. |
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
:
mp.openapi.extensions.smallrye.operationIdStrategy=METHOD
You will now see the operationId
in the schema document for every operation:
---
openapi: 3.0.3
# Header omitted...
/jax-rs/hello:
get:
tags:
- JAX-RS Resource
operationId: helloJaxRs (1)
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Greeting'
post:
tags:
- JAX-RS Resource
operationId: newHelloJaxRs (1)
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 (1)
parameters:
- name: message
in: path
required: true
schema:
type: string
responses:
"204":
description: No Content
1 | Auto generated operationId |
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 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
:
mp.openapi.extensions.smallrye.openapi=3.0.2
Now the version generated will be:
---
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 OpenAPI, 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
:
<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:
@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) {
// Here do the delete...
}
}
The Spring annotations will be scanned and this will be added to your schema document:
---
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
:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-web</artifactId>
</dependency>
Now you can create the endpoint:
@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) {
// Here do the delete...
}
}
and now your Vert.x Routes are available in OpenAPI:
---
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:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-undertow</artifactId>
</dependency>
We can now create a Servlet Endpoint like this for example:
@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
:
---
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:
---
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 OpenAPI project and let’s discuss it there.