git clone https://github.com/openliberty/guide-microprofile-openapi.git
cd guide-microprofile-openapi
Documenting RESTful APIs
Prerequisites:
Explore how to document and filter RESTful APIs from code or static files by using MicroProfile OpenAPI.
What you’ll learn
You will learn how to document and filter RESTful APIs from annotations, POJOs, and static OpenAPI files by using MicroProfile OpenAPI.
The OpenAPI specification, previously known as the Swagger specification, defines a standard interface for documenting and exposing RESTful APIs. This specification allows both humans and computers to understand or process the functionalities of services without requiring direct access to underlying source code or documentation. The MicroProfile OpenAPI specification provides a set of Java interfaces and programming models that allow Java developers to natively produce OpenAPI v3 documents from their JAX-RS applications.
You will document the RESTful APIs of the provided inventory
service, which serves two endpoints, inventory/systems
and inventory/properties
. These two endpoints function the same way as in the other MicroProfile guides.
Before you proceed, note that the 1.0 version of the MicroProfile OpenAPI specification does not define how the /openapi
endpoint may be partitioned in the event of multiple JAX-RS applications running on the same server. In other words, you must stick to one JAX-RS application per server instance as the behaviour for handling multiple applications is currently undefined.
Getting started
The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:
The start
directory contains the starting project that you will build upon.
The finish
directory contains the finished project that you will build.
Try what you’ll build
The finish
directory in the root of this guide contains the finished application. Give it a try before you proceed.
To try out the application, first go to the finish
directory and run the following Maven goal to build the application and deploy it to Open Liberty:
cd finish
mvn liberty:run
After you see the following message, your application server is ready:
The defaultServer server is ready to run a smarter planet.
Next, point your browser to the http://localhost:9080/openapi URL and you’ll see the RESTful APIs of the inventory
service. You can also point to the http://localhost:9080/openapi/ui URL for a more interactive view of the deployed APIs. This UI is built from the Open Source Swagger UI and renders the generated /openapi
document into a very user friendly page.
After you are finished checking out the application, stop the Open Liberty server by pressing CTRL+C
in the command-line session where you ran the server. Alternatively, you can run the liberty:stop
goal from the finish
directory in another shell session:
mvn liberty:stop
Generating the OpenAPI document for the inventory service
You can generate an OpenAPI document in various ways. First, because all JAX-RS annotations are processed by default, you can augment your existing JAX-RS annotations with OpenAPI annotations to enrich your APIs with a minimal amount of work. Second, you can use a set of predefined models to manually create all elements of the OpenAPI tree. Finally, you can filter various elements of the OpenAPI tree, changing them to your liking or removing them entirely.
Navigate to the start
directory to begin.
When you run Open Liberty in development mode, known as dev mode, the server listens for file changes and automatically recompiles and deploys your updates whenever you save a new change. Run the following goal to start Open Liberty in dev mode:
mvn liberty:dev
After you see the following message, your application server in dev mode is ready:
Press the Enter key to run tests on demand.
Dev mode holds your command-line session to listen for file changes. Open another command-line session to continue, or open the project in your editor.
Because the JAX-RS framework handles basic API generation for JAX-RS annotations, a skeleton OpenAPI tree will be generated from the inventory
service. You can use this tree as a starting point and augment it with annotations and code to produce a complete OpenAPI document.
Now, point to the http://localhost:9080/openapi URL to see the generated OpenAPI tree. You can also point to the http://localhost:9080/openapi/ui URL for a more interactive view of the APIs.
Augmenting the existing JAX-RS annotations with OpenAPI annotations
Because all JAX-RS annotations are processed by default, you can augment the existing code with OpenAPI annotations without needing to rewrite portions of the OpenAPI document that are already covered by the JAX-RS framework.
Update theInventoryResource
class.src/main/java/io/openliberty/guides/inventory/InventoryResource.java
Add OpenAPI @APIResponses
, @Operation
and @Parameter
annotations to the two JAX-RS methods, getPropertiesForHost()
and listContents()
.
InventoryResource.java
Clearly, there are many more OpenAPI annotations now, so let’s break them down:
Annotation | Description |
---|---|
| A container for multiple responses from an API operation. This annotation is optional, but it can be helpful to organize a method with multiple responses. |
| Describes a single response from an API operation. |
| Provides a schema and examples for a particular media type. |
| Defines the input and output data types. |
| Describes a single API operation on a path. |
| Describes a single operation parameter. |
Since the Open Liberty server was started in development mode at the beginning of the guide, your changes were automatically picked up. Refresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree. The two endpoints at which your JAX-RS methods are served are now more meaningful:
/inventory/systems/{hostname}:
get:
summary: Get JVM system properties for particular host
description: Retrieves and returns the JVM system properties from the system
service running on the particular host.
operationId: getPropertiesForHost
parameters:
- name: hostname
in: path
description: The host for whom to retrieve the JVM system properties for.
required: true
schema:
type: string
example: foo
responses:
404:
description: Missing description
content:
text/plain: {}
200:
description: JVM system properties of a particular host.
content:
application/json:
schema:
$ref: '#/components/schemas/Properties'
/inventory/systems:
get:
summary: List inventory contents.
description: Returns the currently stored host:properties pairs in the inventory.
operationId: listContents
responses:
200:
description: host:properties pairs stored in the inventory.
content:
application/json:
schema:
$ref: '#/components/schemas/InventoryList'
OpenAPI annotations can also be added to POJOs to describe what they represent. Currently, your OpenAPI document doesn’t have a very meaningful description of the InventoryList
POJO and hence it’s very difficult to tell exactly what that POJO is used for. To describe the InventoryList
POJO in more detail, augment the src/main/java/io/openliberty/guides/inventory/model/InventoryList.java
file with some OpenAPI annotations.
Update theInventoryList
class.src/main/java/io/openliberty/guides/inventory/model/InventoryList.java
Add OpenAPI @Schema
annotations to the InventoryList
class and the systems
variable.
InventoryList.java
Likewise, annotate the src/main/java/io/openliberty/guides/inventory/model/SystemData.java
POJO, which is referenced in the InventoryList
class.
Update theSystemData
class.src/main/java/io/openliberty/guides/inventory/model/SystemData.java
Add OpenAPI @Schema
annotations to the SystemData
class, the hostname
variable and the properties
variable.
SystemData.java
Refresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree:
components:
schemas:
InventoryList:
required:
- systems
type: object
properties:
systems:
type: array
items:
$ref: '#/components/schemas/SystemData'
total:
type: integer
description: POJO that represents the inventory contents.
SystemData:
required:
- hostname
- properties
type: object
properties:
hostname:
type: string
properties:
type: object
additionalProperties:
type: string
description: POJO that represents a single inventory entry.
Properties:
type: object
additionalProperties:
type: string
Filtering the OpenAPI tree elements
Filtering of certain elements and fields of the generated OpenAPI document can be done by using the OASFilter
interface.
Create theInventoryOASFilter
class.src/main/java/io/openliberty/guides/inventory/filter/InventoryOASFilter.java
InventoryOASFilter.java
The filterAPIResponse()
method allows filtering of APIResponse
elements. When you override this method, it will be called once for every APIResponse
element in the OpenAPI tree. In this case, you are matching the 404
response that is returned by the /inventory/systems/{hostname}
endpoint and setting the previously missing description. To remove an APIResponse
element or another filterable element, simply return null
.
The filterOpenAPI()
method allows filtering of the singleton OpenAPI
element. Unlike other filter methods, when you override filterOpenAPI()
, it is called only once as the last method for a particular filter. Hence, make sure that it doesn’t override any other filter operations that are called before it. Your current OpenAPI document doesn’t provide much information on the application itself or on what server and port it runs on. This information is usually provided in the info
and servers
elements, which are currently missing. Use the OASFactory
class to manually set these and other elements of the OpenAPI tree from the org.eclipse.microprofile.openapi.models
package. The OpenAPI
element is the only element that cannot be removed since that would mean removing the whole OpenAPI tree.
Each filtering method is called once for each corresponding element in the model tree. You can think of each method as a callback for various key OpenAPI elements.
Before you can use the filter class that you created, you need to create the microprofile-config.properties
file.
Create the configuration file.
src/main/webapp/META-INF/microprofile-config.properties
microprofile-config.properties
This configuration file is picked up automatically by MicroProfile Config and registers your filter by passing in the fully qualified name of the filter class into the mp.openapi.filter
property.
Refresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree:
info:
title: Inventory App
description: App for storing JVM system properties of various hosts.
license:
name: Eclipse Public License - v 1.0
url: https://www.eclipse.org/legal/epl-v10.html
version: 1.0.0
servers:
- url: http://localhost:{port}
description: Simple Open Liberty.
variables:
port:
default: "9080"
description: Server HTTP port.
404:
description: Invalid hostname or the system service may not be running on
the particular host.
content:
text/plain: {}
For more information about which elements you can filter, see the MicroProfile API.
To learn more about MicroProfile Config, visit the MicroProfile Config GitHub repository and try one of the MicroProfile Config guides.
Using pregenerated OpenAPI documents
As an alternative to generating the OpenAPI model tree from code, you can provide a valid pregenerated OpenAPI document to describe your APIs. This document must be named openapi
with a yml
, yaml
, or json
extension and be placed under the META-INF
directory. Depending on the scenario, the document might be fully or partially complete. If the document is fully complete, then you can disable annotation scanning entirely by setting the mp.openapi.scan.disable
MicroProfile Config property to true
. If the document is partially complete, then you can augment it with code.
To use the pre-generated OpenAPI document, create the OpenAPI document YAML file.
Create the OpenAPI document file.
src/main/webapp/META-INF/openapi.yaml
openapi.yaml
1openapi: 3.0.0
2info:
3 title: Inventory App
4 description: App for storing JVM system properties of various hosts.
5 license:
6 name: Eclipse Public License - v 1.0
7 url: https://www.eclipse.org/legal/epl-v10.html
8 version: 1.0.0
9servers:
10- url: http://localhost:{port}
11 description: Simple Open Liberty.
12 variables:
13 port:
14 default: "9080"
15 description: Server HTTP port.
16paths:
17 /inventory/systems:
18 get:
19 summary: List inventory contents.
20 description: Returns the currently stored host:properties pairs in the inventory.
21 operationId: listContents
22 responses:
23 200:
24 description: host:properties pairs stored in the inventory.
25 content:
26 application/json:
27 schema:
28 $ref: '#/components/schemas/InventoryList'
29 /inventory/systems/{hostname}:
30 get:
31 summary: Get JVM system properties for particular host
32 description: Retrieves and returns the JVM system properties from the system
33 service running on the particular host.
34 operationId: getPropertiesForHost
35 parameters:
36 - name: hostname
37 in: path
38 description: The host for whom to retrieve the JVM system properties for.
39 required: true
40 schema:
41 type: string
42 example: foo
43 responses:
44 404:
45 description: Invalid hostname or the system service may not be running on
46 the particular host.
47 content:
48 text/plain: {}
49 200:
50 description: JVM system properties of a particular host.
51 content:
52 application/json:
53 schema:
54 $ref: '#/components/schemas/Properties'
55 /inventory/properties:
56 get:
57 operationId: getProperties
58 responses:
59 200:
60 description: JVM system properties of the host running this service.
61 content:
62 application/json:
63 schema:
64 type: object
65 additionalProperties:
66 type: string
67components:
68 schemas:
69 InventoryList:
70 required:
71 - systems
72 type: object
73 properties:
74 systems:
75 type: array
76 items:
77 $ref: '#/components/schemas/SystemData'
78 total:
79 type: integer
80 description: POJO that represents the inventory contents.
81 SystemData:
82 required:
83 - hostname
84 - properties
85 type: object
86 properties:
87 hostname:
88 type: string
89 properties:
90 type: object
91 additionalProperties:
92 type: string
93 description: POJO that represents a single inventory entry.
94 Properties:
95 type: object
96 additionalProperties:
97 type: string
This document is the same as your current OpenAPI document with extra APIs for the /inventory/properties
endpoint. Since this document is complete, you can also add the mp.openapi.scan.disable
property and set it to true
in the src/main/webapp/META-INF/microprofile-config.properties
file.
Update the configuration file.
src/main/webapp/META-INF/microprofile-config.properties
Add and set the mp.openapi.scan.disable
property to true
.
microprofile-config.properties
1# tag::Scan[]
2mp.openapi.scan.disable = true
3# end::Scan[]
4# tag::Config[]
5mp.openapi.filter = io.openliberty.guides.inventory.filter.InventoryOASFilter
6# end::Config[]
Refresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree:
/inventory/properties:
get:
operationId: getProperties
responses:
200:
description: JVM system properties of the host running this service.
content:
application/json:
schema:
type: object
additionalProperties:
type: string
Testing the service
No automated tests are provided to verify the correctness of the generated OpenAPI document. Manually verify the document by visiting the http://localhost:9080/openapi or the http://localhost:9080/openapi/ui URL.
A few tests are included for you to test the basic functionality of the inventory
service. If a test failure occurs, then you might have introduced a bug into the code. These tests will run automatically as a part of the integration test suite.
Running the tests
Because you started Open Liberty in dev mode, press the enter/return
key to run the tests.
You will see the following output:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.system.SystemEndpointIT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.4 sec - in it.io.openliberty.guides.system.SystemEndpointIT
Running it.io.openliberty.guides.inventory.InventoryEndpointIT
[WARNING ] Interceptor for {http://client.inventory.guides.openliberty.io/}SystemClient has thrown exception, unwinding now
Could not send Message.
[err] The specified host is unknown: java.net.UnknownHostException: UnknownHostException invoking http://badhostname:9080/inventory/properties: badhostname
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.264 sec - in it.io.openliberty.guides.inventory.InventoryEndpointIT
Results :
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
The warning and error messages are expected and result from a request to a bad or an unknown hostname. This request is made in the testUnknownHost()
test from the InventoryEndpointIT
integration test.
When you are done checking out the service, exit dev mode by pressing CTRL+C
in the command-line session where you ran the server, or by typing q
and then pressing the enter/return
key.
Great work! You’re done!
You have just documented and filtered the APIs of the inventory
service from both the code and a static file by using MicroProfile OpenAPI in Open Liberty.
Feel free to try one of the related MicroProfile guides. They demonstrate additional technologies that you can learn and expand on top of what you built here.
For more in-depth examples of MicroProfile OpenAPI, try one of the demo applications available in the MicroProfile OpenAPI GitHub repository.
Guide Attribution
Documenting RESTful APIs by Open Liberty is licensed under CC BY-ND 4.0
Prerequisites:
Nice work! Where to next?
What did you think of this guide?




Thank you for your feedback!
What could make this guide better?
Raise an issue to share feedback
Create a pull request to contribute to this guide
Need help?
Ask a question on Stack Overflow