It is a no-brainer to provide JSON REST endpoints in a Spring Boot application, just like many state-of-the-art frameworks. Just include the Spring Boot Web Starter as a dependency, define a class annotated with @RestController, and implement at least one method annotated with @RequestMapping (or a specific annotation) as a handler for HTTP requests.
However, in a full API lifecycle, there is more to be done. In this article, we introduce an automated tool chain that helps professionalize Spring Boot REST API development and automates a lot of manual efforts.
Code-First But with a specification
In the API community, much like any traditional type of interface, it is often discussed whether to start with an interface specification and implement the rendering API afterwards (API-Design-First) or to generate the specification from the implementation (Code-First). Both approaches are justifiable. In this article, we will focus on the use cases with a code-first approach, as is often done with Spring Boot.
The figure below illustrates tahe with a code-first approach, first, a Spring Boot application is set up (1), then, REST endpoints and implicitly an API is created (2). Sometimes an API documentation is written, for example according to the OpenAPI standard (3) and occasionally an API client is created as well (4).
Example: User API
As an case for this article, we provide a User API example project. This services as a reference for the presented tool chain and will likely support you in setting up the tool chain for your own context.
The API allows to create, update, and query users. For this purpose, REST operations are provided in an HTTP compliant manner using GET, POST, PUT or DELETE.
While relevant code snippets are included below, the code for the complete case is publicly available on GitHub: https://github.com/viadee/api-roundtrip-spring-boot
API-Roundtrip – The Full-Service-Package
In this article, we refer to an API roundtrip as the development of a complete API package including the following artifacts:
The API itself
An API documentation as an OpenAPI specification
A client for using the API that can be easily integrated in an application
- Integration Tests
That automatically test the functionality of the API after deployment, for instance in a test environment
It is fairly established for other interface types such as SOAP or CORBA to provide documentation (WSDL) and pre-built clients. However, such artifacts are far from being common for REST APIs. A significant reason for this is the lack of established automation - or at least the established automation not being commonly known.
Nowadays, tools such as Postman, SOAP UI or REST Assured are often used for integration tests. They all have allowance for sending JSON requests to a REST API.
Because of a necessity of creating and maintaining JSON requests as well as checks for the resulting JSON responses, this often results in high manual effort compared to the original API development.
Often, even a small adjustment in the Spring boot application involves a high manual and often annoying effort considering the overall package.
AUTOMATION THANKS TO SPRINGDOC AND OPENAPI GENERATOR
For an effective REST API development improving the status quo, an automated tool chain providing capabilities, at least known from using SOAP and CORBA, is desirable. On one hand, this automates repetitive manual tasks, on the other hand, this resolves the media break in the integration test development.
The latter is achieved when integration tests are implemented in Java, like the Spring Boot application itself.
The following illustration provides an overview of the tool chain we describe in this article.
First, an OpenAPI documentation is generated using SpringDoc based on the Spring Boot REST API implementation. In the second step, appropriate client code and integration test code is generated with OpenAPI Generator facilitating the API documentation.
Of course, implementing the actual test logic still requires manual effort.
In detail, the tool chain consists of the following components:
- Spring Boot
Implements the REST-API (1)
Generates the API documentation as an OpenAPI specification and a Swagger UI facilitating the Spring Boot application infrastructure. (2)
Generates the code for API client(s) and integration tests from the OpenAPI specification generated by SpringDoc. (3)
Orchestrates and executes the tools respectively with their concurring plugins. Of course, a similar chain can be built with Gradle.
- Integration Tests
The integration test execution depends on the concrete environment. In this article, we provide an example, where integration tests are triggered with Maven within a deployment pipeline after the deployment itself. (4)
API-Roundtrip – The Tool Chain in a Nutshell
Spring Boot API Implementation
The User API is defined by an interface with corresponding spring annotations. The method names should be chosen with care. By default, they will be used as operationID in the OpenAPI Spec and through this, define the method names of the generated API clients.
It is recommended to explicitly specify a @ResponseStatus as this will automatically result in a cleaner OpenAPI generation later.
The API interface is implemented by a Spring @RestController. For the sake of simplicity, we implemented the storage of the user data with a non-persistent HashMap.
Finally, Maven dependencies must be added in the pom file and a @SpringBootApplication must be created and started. This is necessary to send a first request to the API via http://localhost:8080/api/users .
Up to this point, this is a simple Spring Boot infrastructure. The actual tool chain for our API roundtrip starts from here.
API DoCumentation Generation
With SOAP Web Services it is quite common to provide a WSDL and get it automatically generated by the web service infrastructure. However, this is not yet available out-of-the-box for REST APIs.
SpringDoc fills this gap by automatically generating an OpenAPI specification and Swagger-UI online documentation by processing the Spring Boot infrastructure and providing additional configuration capabilities.
To be able to process SpringDoc in your own application, the following Maven-Dependency is sufficient as it indirectly refers the SpringDoc core as well:
This means that a Swagger UI can be reached immediately after a (new) start of the Spring Boot application: http://localhost:8080/swagger-ui.html
An OpenAPI specification can also be retrieved: http://localhost:8080/v3/api-docs
Both types of documentation can be extensively configured and supplemented.
SpringDoc provides various annotations, such as the @OpenAPIDefinition, for enriching the content of the API documentation as shown in the example below:
It is recommendation to add the server specification at this point. Later, during the OpenAPI file generation, the API will be started on a random free port and without explicitly specifying it here, a rather unfamiliar PORT specification will end up in the API specification.
Of course, if the final URL of the API endpoint is already known, it should be entered here.
If the JSON of the OpenAPI specification need to be formatted by a "pretty-printer" with indentations and line breaks, the following configuration should be added in the Spring Boot application.properties or yaml file:
If needed, the URLs of the Swagger UI and the OpenAPI documentation can be set under the actuator or any other context.
Finally, the OpenAPI specification must be stored as a local file to make it accessible for the client code generation later. SpringDoc comes with a Maven plugin for this step and through this, it provides more convenience compared to similar tools such as Spring Fox.
In a typical Spring Boot configuration, entire dependencies are loaded and resolved at runtime only and thus a running application is needed by the SpringDoc Maven plugin. The plugin downloads the OpenAPI specification from the URL mentioned above.
To get this working, the SpringDoc Maven plugin is configured in the Maven "integration test" phase as shown in the example below:
In addition to the URL for downloading the OpenAPI specification, the target file can also be defined via outputFileName and outputDir parameters. We omitted these parameters in the example, so the file is written by default to target/openapi.json.
To ensure that the Spring Boot application can successfully be launched for the "integration test" phase, we first determine a free port using the codehaus build-helper-maven-plugin. The free port is then configured in the spring-boot-maven-plugin in the "pre-integration-test" phase to start the Spring Boot application (<goal>start</goal>) and stop it during the “post-integration-test” phase (<goal>stop</goal>).
The dynamic port selection ensures that the Maven tool chain works, even if an application is already running on the standard port 8080.
Now, the first step of generating an API documentation is done. Next, we will focus on generating the code for the API client and integration tests.
API Client And Integrationstest Generation
OpenAPI specifications provide a detailed interface description which is sufficient to implement valid requests to the API endpoints. OpenAPI Generator is a quite an active and mature open-source tool for generating code and other artifacts based on these specifications. We use it in the roundtrip tool chain for generating code that encapsulates the REST API calls as easy-to-use Java interfaces.
OpenAPI Generator was derived (forked) from the Swagger Code Gen project aiming at a significantly lighter and faster development. The versions released so far, and the active community of the OpenAPI Generator confirm to the founder’s goal.
Meanwhile, there are over 130 different code generators, for both client and server implementations, as well as for documentation and other API and data formats such as GraphQL or protobuf.
OpenAPI comes with a Maven plugin, so the code generation can be integrated in the overall workflow straight away. A plugin for Gradle is available, too. The following example shows a concurrent Maven plugin configuration:
This example shows a plugin configuration using the Java code generator with the java8 DateLibrary option, generating date data fields as java.util.LocalDate.
In addition, the various packages for the target code are set and the generator is configured to generate only the client source code and no further documentation or Gradle and Maven artifacts.
Unfortunately, in the current version of OpenAPI Generator, the infrastructure classes to be generated still need to be specified because a feature request for a "Client Code only" setting is still open.
To combine the code generation with the previous generation of the OpenAPI specification, there are two things to consider:
- In the parent maven module, the api sub-module must be placed in before the client/integration test sub-modules
- The code generator's path of the OpenAPI specification (<inputSpec>) must refer to the previously downloaded specification
To compile a Java client code generated with the "native" OpenAPI Generator setting, a few dependencies are required.
We provide a minimal configuration for this as a part of the example application on GitHub: https://github.com/viadee/api-roundtrip-spring-boot/blob/main/client/pom.xml
Integration test Execution
If the integration tests are generated in addition to the pure client code, one valid strategy is to implement them as unit tests, but running them separately with the Maven failsafe and not with the classic surefire plugin. The whole setup becomes even more practical if the integration tests are in a separate maven module, which specifies extra rules such as not bundling integration tests with the overall application.
The failsafe plugin is a part of the well-established surefire plugin but it runs in the Maven integration-tests phase by default.
By default, the failsafe plugin comes with a name filter specifying the unit test classes to be executed. The following code example extends the name filter for the integration tests according to the pattern “*IntegrationTest.java”:
Additionaly, the following Maven configuration ensures that the integration tests are not run by the surefire plugin during the normal testing phase and the integration test code is neither deployed nor installed in the Maven repository.
In the sample project provided on GitHub, we have encapsulated the integration test execution in a separate profile to gain even more control when we want to run it within a deployment pipeline.
REST APIs have been adopted widely because of their simplicity. However, this simplicity also comes with many challenges, especially for maintenance and sustainability.
The OpenAPI standard pushed REST API development and management to a new level and the generators and integrations based on it, promise ways out of the maintenance dilemma.
For those who actively want to extend their professional expertise in API development and API management, we think it's worth looking at OpenAPI, SpringDoc, and OpenAPI Generator.
Modern APIs are stand-alone products that significantly enable completely new digital business models and processes. The increasingly conscious handling of high-quality APIs helps to tap this great potential.
Similarly, automation, such as the tool chain presented in the article, helps to develop new quality standards with significantly less manual effort.
Furthermore, OpenAPI specifications provide the base for many other use cases such as public or private API directories or API quality analysis for better, more homogeneous API products.