Yes, we're now running our Black Friday Sale. All Access and Pro are 33% off until 2nd December, 2025:
Generating HTTP Clients in Spring Boot from OpenAPI Spec
Last updated: October 7, 2025
1. Introduction
It’s a common task to implement services that demand some form of network communication. In such cases, we typically need to write server and client code to enable that communication.
In this article, we’ll learn how to automatically generate server and client code using the OpenAPI Specification, focusing on the client-generated code.
2. Defining a Demo API in YAML
The OpenAPI Specification utilizes a YAML file definition to describe an API’s structure. Both server and client applications can seamlessly import that definition to generate server and client code.
To illustrate client-code generation, let’s define a demo weather API using a weatherapi.yaml file:
openapi: 3.0.3
info:
title: Current Weather API
description: |
Get real-time weather information for cities worldwide.
version: 1.0.0
paths:
/weather:
get:
summary: Get current weather data
description: Retrieve current weather information for a specified city
operationId: getCurrentWeather
parameters:
- name: city
in: query
required: true
schema:
type: string
- name: units
in: query
required: false
schema:
type: string
enum: [ celsius, fahrenheit ]
default: celsius
responses:
'200':
description: Successful weather data retrieval
content:
application/json:
schema:
$ref: '#/components/schemas/WeatherResponse'
'404':
description: City not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
WeatherResponse:
type: object
required:
- location
properties:
current:
type: object
required:
- temperature
- units
properties:
temperature:
type: number
format: double
units:
type: string
enum: [ celsius, fahrenheit ]
timestamp:
type: string
format: date-time
ErrorResponse:
type: object
required:
- code
- message
properties:
code:
type: string
message:
type: string
The API has a single REST endpoint that gets the current weather and returns a success response or an error.
3. Configuring the Project
We’ll need a few dependencies and a Maven plugin configuration to generate Java code from our YAML file.
3.1. Adding the Maven Dependencies
To compile the generated code correctly, we’ll need the spring-boot-starter-web and spring-starter-validation dependencies to get a few web classes and annotations from there:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
3.2. Configuring the OpenAPI Maven Plugin
We should also set the openapi-generator-maven-plugin execution in the OpenAPI dependency to generate client code:
<plugins>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>${openapi-generator.version}</version>
<executions>
<execution>
<id>generate-weather-api</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/api/weatherapi.yaml</inputSpec>
<generatorName>spring</generatorName>
<configOptions>
<openApiNullable>false</openApiNullable>
<useJakartaEe>false</useJakartaEe>
<documentationProvider>none</documentationProvider>
<annotationLibrary>none</annotationLibrary>
<apiPackage>com.baeldung.tutorials.openapi.generatehttpclients.api</apiPackage>
<modelPackage>com.baeldung.tutorials.openapi.generatehttpclients.api.model</modelPackage>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
In the snippet above, we defined a Maven execution to generate the code from our YAML file from the configuration set:
- inputSpec: the location of our YAML file
- generatorName: a generator from the ones available. We’re typically interested in either the java or spring generators
- openApiNullable: whether the code should use Jackson Nullable library or not
- useJakartaEe: whether the code should use the jakarta namespace instead of javax
- documentationProvider: the source of documentation for the API. It’s either none or source. The latter means that it’ll document as described in the YAML file.
- annotationLibrary: the annotation library to use in documentation if documentationProvider is set to non-none
- apiPackage: the target package for API-related classes
- modelPackage: the target package for model classes
Another important configOption that was not listed there is library. This is the library that the generated code uses for implementation.
We’re using the default library, a simple test implementation using Spring’s vanilla web and http packages. For that article, we’re only interested in getting a ready-to-use REST client based on our OpenAPI Specification file, so that basic test implementation works for illustrative purposes.
OpenAPI provides different options for library by setting the generatorName to spring, such as implementations using auto-configured Spring Cloud OpenFeign beans.
Additionally, setting generatorName to java provides a broader set of options for the library we want to generate code for, including RestTemplate, OpenFeign, and RestAssured.
4. Generating API Clients
After setting up basic OpenAPI configuration, we can generate code using the Maven plugin by running:
mvn compile
After that, we’ll be able to see the generated classes under the path in the target folder:
We can see two important files there: the WeatherApi interface and the WeatherApiController. Let’s glance at how OpenAPI generator generated WeatherApi:
public interface WeatherApi {
@RequestMapping(
method = RequestMethod.GET,
value = "/weather",
produces = { "application/json" }
)
default ResponseEntity<WeatherResponse> getCurrentWeather(
@NotNull @Valid @RequestParam(value = "city", required = true) String city,
@Valid @RequestParam(value = "units", required = false, defaultValue = "celsius") String units
) {
// a generated test implementation
}
}
After removing some clutter from the generated code, we get the getCurrentWeather() method, which defines the endpoint at /weather using the @RequestMapping annotation. That method serializes, validates, and provides defaults for the input as we defined in our YAML specification, using @Valid, @NotNull, and @RequestParam annotations.
Then, if we look at the WeatherApiController:
@Controller
@RequestMapping("${openapi.currentWeather.base-path:}")
public class WeatherApiController implements WeatherApi {
// a generated test implementation
}
OpenAPI also generated a Spring auto-configured implementation of WeatherApiController, which uses an environment variable in @RequestMapping, so clients that import that bundle into their projects can set the API base path via configuration. For instance, at the client side, we could import an exported module that has the api generated package and just set a base-path resource and inject WeatherApi automatically:
@Service
public class GetWeatherService {
private final WeatherApi weatherApi;
public GetWeatherService(WeatherApi weatherApi) {
this.weatherApi = weatherApi;
}
public WeatherResponse getCurrentWeather(String city, String units) {
var response = weatherApi.getCurrentWeather(city, units);
if (response.getStatusCodeValue() < 399) {
return response.getBody();
}
throw new RuntimeException("Failed to get current weather for " + city);
}
}
And at the client’s application.yaml:
openapi:
currentWeather:
base-path: https://localhost:8080
5. Advantages of OpenAPI
The OpenAPI generator is handy when a service exposes an endpoint and has clients that depend on it. In that case, we can maintain two modules, one for clients and another for the server, both of which read the API contract from the same OpenAPI YAML specification.
Hence, both modules would be up-to-date with the latest API contract, thereby minimizing the risk of breaking the contract between client and server applications. That requires a more granular management of the client module versioning, and requires that clients keep the library up-to-date on their side.
Notably, using code generators provides us with the advantage of skipping part of the manual code implementation. Thus, we can reduce the effort required to create communication between the client and server, as the code on both sides can be auto-generated.
For instance, in a situation where the same developer needs to implement both the server and client code, it could be productive to reduce those two efforts to just creating the OpenAPI YAML specification file and let the generator do its job.
6. Drawbacks of OpenAPI
As with any code generator, we have the drawback of not having total control over how the generated code looks. For more customized and complex client code needs, it can be painful to use generators, as we lack control over what’s being generated.
For instance, OpenAPI currently generates mutable model classes for API responses, which it could instead leverage Java Records for simplicity and safety.
Additionally, with generated code, we also lack control over debugging, troubleshooting, and fixing code, as we can’t modify it.
Finally, it introduces another dependency with a set of transitive dependencies, which requires more effort to update and avoid incompatibility and security issues.
7. Conclusion
In this article, we’ve seen how to auto-generate client code using OpenAPI Specification and the OpenAPI Maven plugin. We’ve addressed the typical case of generating simple REST clients using the spring-web dependency and the API described in an OpenAPI Specification YAML file.
We’ve also examined the advantages and disadvantages of using such a library to generate code for our use.
The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.















