Easily Integrate OpenAPI with a Spring Boot Project: A Simple Setup Guide
Spring Boot is a remarkable framework that simplifies the setup and development of standalone, production-grade Spring-based applications. In this article, we will walk through the steps to set up a simple Spring Boot project that uses OpenAPI specification files to generate API interfaces and request/response schemas.
Prerequisites
Before we start, ensure that you have the following installed on your machine:
- Java v21
- Spring boot v3.2.5
- Gradle v8.7
- IntelliJ IDEA or any other preferred IDE
Step 1: Create a New Spring Boot Project
You can get started with the Spring Initializr tool. For this project, you need to add the following dependencies: ‘Spring Web’, ‘Spring DevTools’ and ‘OpenAPI 3’.
This article follows this repository for demonstration purposes. You can refer to the dependencies defined in the build.gradle
file of this repository for your project.
Step 2: Setting Up the OpenAPI Specification
Once you have your project set up, you can start to define your OpenAPI specification. This will define your API interfaces and request/response schemas. You can do this in a YAML or JSON file. For simplicity, we’ll do this in a YAML file. I have placed all specification files in specs
directory. The routes.yaml
file has configurations and path routes defined, the sub folders stores the properties of these paths. It is a good practice to use $ref
and separate the concerns like route properties, request and response schemas into distinct files.
For this article, we are creating only a healthcheck endpoint to demonstrate setup:
openapi: "3.0.3"
info:
description: "example-project"
version: "1.1.0"
title: "example-project"
servers:
- url: "<http://localhost:8080>"
variables:
basePath:
default: /api/v1
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWTpaths:
/healthcheck:
$ref: "./paths/healthcheck/index.yaml"
from a glance we can see the benefit of using reference, this will make it easy to add more paths and organise them in one place for easy reference.
We will now write the HTTP methods for this endpoint, since we just want to fetch information of application health, using GET is appropriate.
get:
summary: "healthcheck"
operationId: healthcheck
responses:
"200":
description: "OK"
content:
application/json:
schema:
$ref: "../../schemas/responses/healthcheck_response.yaml"
One advantage of using reference in response schema is that, when API Model will be generated, the name of class will be yaml file name converted to camel case.
We now define response schema to be something simple:
type: object
required:
- status
properties:
status:
description: ok
type: string
msg:
description: test
type: string
Refer to OpenAPI 3.0 documentation to understand more about writing spec files (https://swagger.io/docs/specification/paths-and-operations).
Step 3: Generate API Interfaces & Request/Response Schemas
To generate the API interfaces and request/response schemas from the OpenAPI specification file, we will configure ‘openapi-generator-gradle-plugin’ for Gradle by adding this to build.gradle
file:
import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.5'
id 'io.spring.dependency-management' version '1.1.4'
id 'org.openapi.generator' version '7.2.0'
}
...sourceSets {
main {
java {
srcDirs 'src/main/java', 'build/generated/src/main/java'
}
}
}repositories {
...
}dependencies {
// other dependencies
...
implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
implementation 'org.springdoc:springdoc-openapi-data-rest:1.7.0'
implementation 'org.openapitools:jackson-databind-nullable:0.2.1'
}...tasks.named('compileJava') {
dependsOn 'openApiGenerate', 'mergeSpec'
}openApiGenerate {
generatorName = 'spring'
inputSpec = "$rootDir/specs/routes.yaml"
outputDir = "$buildDir/generated"
apiPackage = "${group}.simpleauth.api"
modelPackage = "${group}.simpleauth.model"
configOptions.set([
useSpringBoot3: "true",
interfaceOnly : "true"
])
}openApiValidate {
inputSpec = "$rootDir/specs/routes.yaml"
}tasks.register('mergeSpec', GenerateTask) {
generatorName = "openapi-yaml"
inputSpec = "$rootDir/specs/routes.yaml"
outputDir = "$rootDir/specs/out"
configOptions.set([
outputFile: 'openapi-gen.yaml'
])
}
First, we have imported the GenerateTask, which is defined in OpenAPI’s plugin for Gradle, and added to the plugins.
Next, we’ve added an additional source directory, build/generated/src..
, which will store the generated stubs.
We’ve then included a dependency on OpenAPI and other required tools.
The compileJava
task, which compiles the main Java source files of the project, depends on two other tasks: openApiGenerate
and mergeSpec
. This means that Gradle will run openApiGenerate
and mergeSpec
before executing compileJava
.
We’ve also utilized the OpenAPI plugin to configure the openApiGenerate
and openApiValidate
tasks, as well as created the mergeSpec
task.
mergeSpec
merges multiple OpenAPI spec files into a single yaml file. The output of this task is then used by the generate task to write API stubs.openApiGenerate
generates the stubs using the OpenAPI specification. The parameters within this block indicate where to find the OpenAPI specification, the location for the generated code, and the package names to use. It also provides specific configuration options for the generator.openApiValidate
validates the OpenAPI specification files.
Generate API Stubs:
Run the following command once OpenAPI setup is done
./gradlew openApiValidate openApiGenerate mergeSpec
Step 5: Healthcheck controller
When inspecting the generated
directory, you'll find these files:
We’ll implement the newly created interface as our HealthCheck
controller in the src
directory.
Take a look at the code generated in HealthcheckAPI
:
/**
* NOTE: This class is auto generated by OpenAPI Generator (<https://openapi-generator.tech>) (7.2.0).
* <https://openapi-generator.tech>
* Do not edit the class manually.
*/
// ...
public interface HealthcheckApi {
// ...
/**
* GET /healthcheck : healthcheck
*
* @return OK (status code 200)
*/
@Operation(
operationId = "healthcheck",
summary = "healthcheck",
responses = {
@ApiResponse(responseCode = "200", description = "OK", content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = HealthcheckResponse.class))
})
}
)
@RequestMapping(
method = RequestMethod.GET,
value = "/healthcheck",
produces = { "application/json" }
)
default ResponseEntity<HealthcheckResponse> healthcheck(
) {
// ...
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
In this interface, the request mapping, response type and API description is generated. We now have to implement the method with required logic.
We will implement this healthcheck()
method declared here; by creating a HealthcheckController
class in src/main/java/controllers
:
@RestController
public class HealthcheckController implements HealthcheckApi {
@Override
public ResponseEntity<HealthcheckResponse> healthcheck() {
HealthcheckResponse response = new HealthcheckResponse()
.status("200")
.msg("Application is Running!");
return ResponseEntity.ok(response);
}
}
In this example, we’re returning a basic response to indicate that the application has started.
Step 6: Running the Application
Once everything is set up, you can run your application. With Spring Boot, this is as simple as running the ‘main’ method in your main class.
In your terminal, the steps for running the application would be:
- Building openAPI stubs:
./gradlew clean openApiValidate openApiGenerate mergeSpec
- Building project:
./gradlew build
- Running the application:
./gradlew bootRun
The server usually starts on 8080 port:
When you open the specified url for healthcheck, it will look like:
We’ve successfully set up a basic Spring Boot project! Now, we can begin adding more features to it.