APIs are expected to enable automation, in turn driving efficiency, consistency and cost savings. API quality matters, both for API consumers and implementers. For these reasons, building an API infrastructure from scratch may not be a good idea. It is much easier and more productive to use a good framework or library, often more than one.
From a development point of view, it's crucial to achieve target whilst ensuring the fulfillment of the following requirements: timing, costs saving and quality. Standardization is one of important way to achieve those goals: to ensure consistency, speed-up development, enable factorization and simplify maintanance.
Furthermore, a very important factor in APIs is a complete and accurate documentation. And best of all, the documentation should be produced and provided in a standard form, to automate the documentation process and to ensure a great overall experience in API consuming.
Even for the simplest API, achieving all these goals and accomplishing all the required tasks may be not so simple for a developer. A minimal infrastructure must be setted up, a set of components has to be selected, configured and enabled to work together. The API implementation stack configuration can be a complex, repetitive and error-prone process. The developer should not take care about the configuration and components integration details, focusing on the business tasks the API has to face and resolve.
It is where standards, frameworks and libraries come to help, taking charge of the API infrastructure setup and configuration and leaving the developer free to focus on the API goals. In one sentence: focus on what matters and forget the tedious stuff.
The Holon Platform can act both as glue for industry-standard libraries and as a catalyst for API development productivity.
In this post, we'll start from the beginning, showing how to create a simple API service. We'll focus on project setup and configuration, leaving out the API business logic concerns. In fact, this API will provide a single "ping" operation, which will respond with a pong
message.
The building blocks we're going to use for this project are:
The code of this example is available on GitHub: https://github.com/holon-platform/spring-boot-jaxrs-swagger-api-example.
Maven is used for project setup and dependency management, so let's start with configuring the project's pom
dependencies.
First of all, we import the Holon Platform BOM (Bill Of Materials), this way we'll no longer need to specify the Holon Platform artifacts version. The right version of each artifact is declared and provided by the platform BOM.
<dependencyManagement>
<dependencies>
<!-- Holon Platform BOM -->
<dependency>
<groupId>com.holon-platform</groupId>
<artifactId>bom</artifactId>
<version>5.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
We'll use the Holon JAX-RS Spring Boot starter with Jersey as implementation, so we declare the following dependency:
<!-- Holon JAX-RS using Jersey -->
<dependency>
<groupId>com.holon-platform.jaxrs</groupId>
<artifactId>holon-starter-jersey</artifactId>
</dependency>
Do you prefer to use Resteasy as JAX-RS implementation? With Holon it's only a matter of configuration, just change the Holon starter to use:
<!-- Holon JAX-RS using Resteasy -->
<dependency>
<groupId>com.holon-platform.jaxrs</groupId>
<artifactId>holon-starter-resteasy</artifactId>
</dependency>
These Holon Spring Boot starters inherit the standard Spring Boot web starters, and provide embedded servlet container configuration, logging and Holon specific auto-configuration facilities. By default, Tomcat is used as embedded servlet container. Want to use Undertow? Just change the starter name (for example, holon-starter-jersey-undertow
). Concerning JSON data format instead, Jackson is automatically setted up. What if Gson is your preferred choice? It's only about changing a name, again (for example, holon-starter-jersey-gson
).
Now, independently from the starter you choosed, we have to create just 2 Java classes. The first one, which we call Application
, is the Spring Boot application entry-point, which triggers auto-configuration and provides the main method used to run the application itself, starting the embedded servlet container and deploying our API endpoints:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
A application.yml
file can be used to provide application configuration properties. For example, to configure the embedded server port:
server:
port: 9999
Next it's time to create the API endpoint, which will provide the API operations listening to the HTTP requests and returning the corresponding responses, using JSON as data format. As said before, this simple API will only provide a ping operation, mapped to the GET
request method, returning a pong
response encoded as JSON. Finally, we want to map the API operations endpoint to the base "/api" path. Using JAX-RS, this is how the API endpoint can be defined:
@Component
@Path("/api")
public class ApiEndpoint {
@GET
@Path("/ping")
@Produces(MediaType.APPLICATION_JSON)
public Response ping() {
return Response.ok("pong").build();
}
}
Note the @Component
annotation on the API endpoint class. This is a standard Spring annotation to declare this class as a Spring component, candidate for auto-detection. Relying on the Holon Platform auto-configuration features, this class will be automatically detected and registered as a JAX-RS resource in Jersey (or Resteasy). And of course, will be enabled as a Spring component, allowing, for example, dependency injections.
To run the application, we'll use the Spring Boot maven plugin, declaring it in the project pom
:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution><goals><goal>repackage</goal></goals></execution>
</executions>
</plugin>
</plugins>
</build>
This way, a runnable jar will be created by the Spring Boot plugin, with all the required dependencies and able to be execute standalone using a JRE.
To test the application, we can use the run Spring Boot maven goal this way:
mvn spring-boot:run
That's it. The application can be now started, activating our API service. The ping operation will be available sending an HTTP GET request to an URL like this:
http://hostname:9999/api/ping
Obtaining a response like the following:
HTTP/1.1 200
Content-Type: application/json
Content-Length: 4
post
We'll use Swagger for API documentation. The Swagger specification is now the foudation of the Open API Initiative, which is trying to standardizing on how REST APIs are described.
The Holon Platform provides many configuration facilities for Swagger, perfectly integrated with JAX-RS and the Spring Boot auto-configuration architecture.
Let's start adding the Holon Platform Swagger depencency to our project pom
:
<dependency>
<groupId>com.holon-platform.jaxrs</groupId>
<artifactId>holon-jaxrs-swagger</artifactId>
</dependency>
From now on, the Holon Platform auto-configuration services will auto-detect any JAX-RS endpoint annotated with the standard Swagger @Api
annotation and automatically create a JAX-RS endpoint to generate and provide the Swagger API documentation. By default, this endpoint will be mapped to the /api-docs
path and supports a type
query parameter to obtain the Swagger API documentation as JSON (the default behaviour) or YAML.
Standard Swagger annotations are supported to enrich and configure the API documentation, such as @ApiOperation
. Additionaly, the Holon Platform Swagger module provides the @ApiDefinition
annotation, which can be used to configure the overall API informations (such the title and the version) and to change the default API documentation endpoint path. In this example, we want to use the /api/docs
path to provide the API documentation.
So you want to modify the ApiEndpoint
class this way:
@ApiDefinition(docsPath = "/api/docs", title = "Example API", version = "v1", prettyPrint = true)
@Api("Test API")
@Component
@Path("/api")
public class ApiEndpoint {
@ApiOperation("Ping request")
@ApiResponses({ @ApiResponse(code = 200, message = "OK: pong", response = String.class) })
@GET
@Path("/ping")
@Produces(MediaType.APPLICATION_JSON)
public Response ping() {
return Response.ok("pong").build();
}
}
This way, the Swagger API documentation can be obtained in JSON format from the URL:
http://hostname:9999/api/docs
Or in the YAML format:
http://hostname:9999/api/docs?type=yaml
Using the Swagger Editor to display the API documentation, it will appear like this:
As a last step, we want to add application monitoring and management capabilities, using the Spring Boot Actuator, which adds several production grade services to obtain application information, health check, configuration and so on.
To enable the Spring Boot Actuator endpoints auto-configuration, we have to add the following dependencies to the project pom
:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.8.RELEASE</version>
</dependency>
Now a set of monitoring endpoints should be registered. For example, the health check endpoint listens to the /health
path. So we expect to obtain the health information performing a GET
request to an URL like this:
http://localhost:9999/health
But wait... things do not go as we expected: the server renponds with a 404
(not found) error code! Why?
This is a well-known and annoying problem which occurs when the Jersey servlet is mapped to the root context path. The problem is that Jersey will get all the request. It does not know that it needs to forward to any actuator endpoints.
This can be resolved by change Jersey to be used as filter instead of a servlet. Using Spring Boot we can do this using a configuration property in the application.yml
file:
spring:
jersey:
type: filter
But this is not enough: we also have to set the Jersey property to forward requests for URLs it doesn't know. With the Holon Platform JAX-RS module this is simple, just another configuration property to set:
holon:
jersey:
forwardOn404: true
Well done, everything work as expected! The health check endpoint is reachable and provides the following JSON response content:
{"status":"UP"}
With 2 Java classes, a pom
and a Spring Boot configuration file we setted up a production grade API application, able to be runned standalone, with API documentation and monitoring capabilities. Now it's time to focus on business tasks, making the API somehow useful!
This this only the first article in a series that shows how to use the Holon Platform to face the API development challenges, including the microservices world, so stay tuned!
The source code of the example API created in this post is available on GitHub: https://github.com/holon-platform/spring-boot-jaxrs-swagger-api-example.
See the Holon Platform examples for other API development related sample projects.