Microprofile OpenAPI allows to describe its API through OpenAPI standard. One interesting consequence of that is that you can directly integrate your API with Swagger UI which now supports OpenAPI format.

To illustrate that we will use a very small application composed of one JAX-RS application binding the endpoints under the /api subcontext:

import javax.enterprise.context.Dependent;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@Dependent
@ApplicationPath("api")
public class Api extends Application {
}

And an HelloEndpoint just returning a message:

import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;

@Tag(name = "Demo", description = "Some endpoint to show how to integrate with swagger-ui")
@Path("hello")
@ApplicationScoped
public class HelloEndpoint {
    @GET
    @APIResponse(description = "The greeting message", content = @Content(mediaType = MediaType.APPLICATION_JSON))
    @Operation(description = "Will return a greeting message.", operationId = "helloendpoint_get")
    @Produces(MediaType.APPLICATION_JSON)
    public Message get() {
        return new Message("Hi");
    }

    public static class Message {
        private final String message;

        private Message(final String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }
    }
}

Here, the fact the @APIResponse is defined is important to ensure it is well generated and exposes the media type in the openapi.json. You will also note this implementation assumes you have JSON-B or equivalent for the mapping (which is available in Microprofile 2.0 only).

In the previous snippet we also put a tag on the endpoint which will be used by the UI to group the endpoint together and the small description will let your end users understand what the endpoints are about. In other words, it is important when you start going the path of exposing your API publicly, to think about humans more than systems and put all the details you can to make it understable. This goes to the point of using @Schema on your model to describe each fields - but this is out of scope of this post.

Now we have an application, we will add swagger-ui. To do that you can use CDN links, a custom frontend build or just a webjars (a jar dependency inclusing the front code). For the context of this post we will use the last option so we will add this dependency:

<dependency>
  <groupId>org.webjars</groupId>
  <artifactId>swagger-ui</artifactId>
  <version>3.17.4</version>
</dependency>

This dependency comes with a default index.html  so it is already usable but it uses a pregenerated application and not our application. To fix that we will write a custom index.html. Don't worry it will stay short. All the trick is to import the swagger resources from the classpath (Servlet 3 and the correct webjars packaging enables you to do that) and just launch swagger ui javascript at the end:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>API UI</title>
    <link rel="stylesheet" type="text/css" href="/webjars/swagger-ui/3.17.4/swagger-ui.css" >
    <link rel="icon" type="image/png" href="/webjars/swagger-ui/3.17.4/favicon-32x32.png" sizes="32x32" />
    <link rel="icon" type="image/png" href="/webjars/swagger-ui/3.17.4/favicon-16x16.png" sizes="16x16" />
    <style>
      html
      {
        box-sizing: border-box;
        overflow: -moz-scrollbars-vertical;
        overflow-y: scroll;
      }

      *,
      *:before,
      *:after
      {
        box-sizing: inherit;
      }

      body
      {
        margin:0;
        background: #fafafa;
      }
    </style>
</head>

<body>
<div id="swagger-ui"></div>

<script src="/webjars/swagger-ui/3.17.4/swagger-ui-bundle.js"> </script>
<script src="/webjars/swagger-ui/3.17.4/swagger-ui-standalone-preset.js"> </script>
<script>
    window.onload = function () {
      window.ui = SwaggerUIBundle({
        url: "/api/openapi.json",
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout"
      });
    };
</script>

</body>
</html>

To make this index.html our homepage, we need to put it in META-INF/resources/index.html in our jar. It will use the Microprofile openapi.json thanks to the url customization we did in the javascript at the end (this is actually the only thing we changed with the title).

Now if you start your application and go on your homepage, you should see:

If you click on the endpoint, you can even test it directly in the UI:

Setting up such a UI is very interesting for end users because they should have a full access to the whole API and be able to test it without having to create any environment (even a ready to run image docker can slow down a ramp up and API discovery).

In the context of this post we set up the API with a webjars dependency, but this is actually something you can use for a documentation. If you use Apache Geronimo OpenAPI Maven plugin you will be able to generate the openapi.json and use it with the swagger UI (which is just some javascript which fetched the json). Add some mock data and you can use all of that with a static website :).

Before closing that post and if you are impatient to test it, you can use this pom and start the application with mvn package meecrowave:run:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.test</groupId>
  <artifactId>demo</artifactId>
  <version>1.0-SNAPSHOT</version>

  <dependencies>
    <dependency>
      <groupId>org.eclipse.microprofile.openapi</groupId>
      <artifactId>microprofile-openapi-api</artifactId>
      <version>1.0.1</version>
      <exclusions>
        <exclusion>
          <groupId>org.osgi</groupId>
          <artifactId>org.osgi.annotation.versioning</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.apache.geronimo</groupId>
      <artifactId>geronimo-openapi-impl</artifactId>
      <version>1.0.1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.meecrowave</groupId>
      <artifactId>meecrowave-core</artifactId>
      <version>1.2.3</version>
    </dependency>

    <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>swagger-ui</artifactId>
      <version>3.17.4</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.meecrowave</groupId>
        <artifactId>meecrowave-maven-plugin</artifactId>
        <version>1.2.3</version>
      </plugin>
    </plugins>
  </build>

  <repositories>
    <repository> <!-- geronimo-openapi 1.0.0 requires jackson dependency to start and 1.0.1 was not released (in vote) when writing this post -->
      <id>geronimo-openapi-staging</id>
      <url>https://repository.apache.org/content/repositories/orgapachegeronimo-1066</url>
    </repository>
  </repositories>
</project>

Happy API-ing ;).

 

 

 

From the same author:

In the same category: