Get More Transparency on Your Microservices API With OpenAPI
Join the DZone community and get the full member experience.
Join For FreeEvery Friday, Platform.sh has a nice tech discussion about several discussions, such as Jakarta EE, Payara, Quarkus, and so on (To check the calendar you can click here). On Episode number 15 there was a great discussion about Open API and Postman. That's worth watching if you don't yet.
First, let's start with Open API definition, from the website it says:
The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.
Hello World Open API With Postman
To absorb that knowledge and practice what we learned from this episode, let's create a Hello world Open API and then test it with Postman. We'll create a small Java API that will handle all Deploy-Fridays episodes and store it on MongoDB. Why do we want to use MongoDB? Just because we're hipsters!!
We're going to be using Eclipse MicroProfile. This will make our life easier when we want to create a microservices application. In this tutorial, we'll use Helidon version 2.0. To integrate with a NoSQL database let's use Jakarta NoSQL.
Eclipse MicroProfile has a subproject to integrate with Open API that's annotation-driven.
Annotation |
Description |
@APIResponses |
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. |
@APIResponse |
Describes a single response from an API operation. |
@Content |
Provides a schema and examples for a particular media type. |
@Schema |
Defines the input and output data types. |
@Operation |
Describes a single API operation on a path. |
@Parameter |
Describes a single operation parameter. |
Let's start with the entities; remember that those entities will also be the data inputs, so we'll define them as a schema.
As you can see in the code, beyond the Eclipse MicroProfile Open API annotations, there are also the Jakarta NoSQL entities annotations and JSON Binding to check the fields instead of methods.
xxxxxxxxxx
import jakarta.nosql.mapping.Column;
import jakarta.nosql.mapping.Entity;
import jakarta.nosql.mapping.Id;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import javax.json.bind.annotation.JsonbVisibility;
import java.util.Set;
name = "Episode", description = "The entity that represents Episode") (
FieldVisibility.class) (
public class Episode {
required = true, name = "id", description = "The episode ID", example = "1") (
private Long id;
required = true, name = "url", description = "The episode URL", example = "https://www.youtube.com/playlist?list=PLn5EpEMtxTCmLsbLgaN3djvEkRdp-YmlE") (
private String url;
required = true, description = "the hosts") (
private Set<String> hosts;
required = true, description = "the guests") (
private Set<Guest> guests;
}
import jakarta.nosql.mapping.Column;
import jakarta.nosql.mapping.Entity;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import javax.json.bind.annotation.JsonbVisibility;
import java.util.Objects;
name = "Guest", description = "The entity that represents Guest") (
FieldVisibility.class) (
public class Guest {
required = true, description = "The guest name", example = "Ada Lovelace") (
private String name;
required = true, description = "The guest twitter handle", example = "adalovelace") (
private String twitter;
}
After creating the entity, the next step to create the resource. As the usual JAX-RS annotation to define the path and the HTTP verbs such as GET, POST, and DELETE, there are also the Open API annotations.
xxxxxxxxxx
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Optional;
import java.util.logging.Logger;
"episodes") (
MediaType.APPLICATION_JSON) (
MediaType.APPLICATION_JSON) (
public class EpisodeResource {
private static final Logger LOGGER = Logger.getLogger(EpisodeResource.class.getName());
private EpisodeRepository repository;
summary = "Get all episodes", description = "Returns all available episodes") (
responseCode = "500", description = "Server unavailable") (
responseCode = "200", description = "The episodes") (
name = "BETA", description = "This API is currently in beta state") (
description = "The episodes", (
responseCode = "200",
content = (mediaType = MediaType.APPLICATION_JSON,
schema = (implementation = Episodes.class,
readOnly = true,
description = "the episodes",
required = true,
name = "Episodes")))
public Episodes getAll() {
return new Episodes(repository.findAll());
}
"{id}") (
summary = "Find an episode by id", description = "Find an episode by id") (
responseCode = "200", description = "The episodes") (
responseCode = "404", description = "When the id does not exist") (
responseCode = "500", description = "Server unavailable") (
name = "BETA", description = "This API is currently in beta state") (
description = "The episode", (
content = (mediaType = MediaType.APPLICATION_JSON,
schema = (implementation = Episode.class)))
public Episode findById( (description = "The Episode ID", required = true,
example = "1", schema = (type = SchemaType.INTEGER)) ("id") Long id) {
LOGGER.info("Finds episode by id: " + id);
final Optional<Episode> episode = repository.findById(id);
return episode.orElseThrow(() -> new WebApplicationException(Response.Status.NOT_FOUND));
}
summary = "Insert an Episode", description = "Insert an Episode") (
responseCode = "201", description = "When creates an episode") (
responseCode = "500", description = "Server unavailable") (
name = "BETA", description = "This API is currently in beta state") (
public void insert( (description = "Create a new Episode.",
content = (mediaType = "application/json",
schema = (implementation = Episode.class))) Episode episode) {
LOGGER.info("Episode: " + episode);
repository.save(episode);
}
"{id}") (
summary = "Delete an episode by ID", description = "Delete an episode by ID") (
responseCode = "200", description = "When deletes the episode") (
responseCode = "404", description = "When the id does not exist") (
responseCode = "500", description = "Server unavailable") (
name = "BETA", description = "This API is currently in beta state") (
description = "The episode", (
content = (mediaType = MediaType.APPLICATION_JSON,
schema = (implementation = Episode.class)))
public void delete( (description = "The Episode ID", required = true,
example = "1", schema = (type = SchemaType.INTEGER))
"id") Long id) { (
LOGGER.info("Deletes episode by id: " + id);
repository.deleteById(id);
}
}
The code is ready, to run locally let's use Docker with the command below:
xxxxxxxxxx
docker run -d --name mongodb-instance -p 27017:27017 mongo
The application is ready, and with MongoDB instance running, you can execute the application:
xxxxxxxxxx
mvn clean package
java -jar target/deploy-friday-ep15.jar
When the application is running, you can access the URL http://localhost:8080/openapi/ to get the open API result.
Let's go to the Postman to test your API; the first step is to import the API from the URL that we already showed.
Let's go to the Postman to test your API. The first step is to install the application itself where it has support for Linux, Windows or Mac OS. When we installed the Postman application, the next step is to import the API from the Open API URL.
It works like magic, don't forget to set the variables inside Postman.
Cloud: To Infinity and Beyond With Platform.sh
The application is ready to go, and you can run the application. The next step is to move to the cloud with Platform.sh.
To move your application to the cloud, briefly, you need three files:
One to define the routes:
xxxxxxxxxx
"https://{default}/":
type upstream
upstream"app:http"
"https://www.{default}/"
type redirect
to"https://{default}/"
One file to define the services that you need in your application.
To point out, you don't need to worry to handle it. Platform.sh will be the infrastructure for you.
xxxxxxxxxx
mongodb
type mongodb3.6
disk1024
The last file is to configure the application, basically, you'll teach how to build and execute your application to Platform.sh. In this sample, we'll create a Java 11 container, build with a maven command, and start the application from an uberjar.
xxxxxxxxxx
name app
type"java:11"
disk1024
hooks
build mvn clean package
relationships
mongodb'mongodb:mongodb'
web
commands
start java -jar $JAVA_OPTS $CREDENTIAL -Dserver.port=$PORT target/deploy-friday-ep15.jar
As you can see, we set the CREDENTIAL
environment. The goal is to overwrite the database credentials on the cloud, so we can have a configuration to run locally and another one to run in the cloud with Platform.sh.
xxxxxxxxxx
export MONGO_PORT=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|jq -r ".mongodb[0].port"`
export MONGO_HOST=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|jq -r ".mongodb[0].host"`
export MONGO_ADDRESS="${MONGO_HOST}:${MONGO_PORT}"
export MONGO_PASSWORD=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|jq -r ".mongodb[0].password"`
export MONGO_USER=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|jq -r ".mongodb[0].username"`
export MONGO_DATABASE=`echo $PLATFORM_RELATIONSHIPS|base64 -d|json_pp|jq -r ".mongodb[0].path"`
export JAVA_MEMORY=-Xmx$(jq .info.limits.memory /run/config.json)m
export JAVA_OPTS="$JAVA_MEMORY -XX:+ExitOnOutOfMemoryError"
export CREDENTIAL="-Ddocument.settings.jakarta.nosql.host=$MONGO_ADDRESS -Ddocument.database=$MONGO_DATABASE -Ddocument.settings.jakarta.nosql.user=$MONGO_USER -Ddocument.settings.jakarta.nosql.password=$MONGO_PASSWORD -Ddocument.settings.mongodb.authentication.source=$MONGO_DATABASE"
The application is now ready, so it’s time to move it to the cloud with Platform.sh using the following steps:
Create a new free trial account.
Sign up with a new user and password, or login using a current GitHub, Bitbucket, or Google account. If you use a third-party login, you’ll be able to set a password for your Platform.sh account later.
Select the region of the world where your site should live.
Select the blank template.
You have the option to either integrate to GitHub, GitLab, or Platform.sh will provide it to you. Finally, push to the remote repository.
The example is ready and in a GitHub repository.
That is it; we finished a tutorial about how to integrate and connect a Java application with Open API and test with Postman. Stay tuned to the next Deploy Friday episodes.
Opinions expressed by DZone contributors are their own.
Comments