Spring Boot OpenAPI generator example

Updated: 2023-12-28

Update: The code has been updated for Spring Boot 3.x and Angular 17

In this example we show how to integrate OpenApi 3 (Swagger) in your Spring Boot application to generate your @RestController using a specification API.

Initially, we will generate only the backend code, in a second example we will generate the TypeScript frontend for Angular.

This tutorial is based on our example of Spring Boot and Angular architecture.

OpenAPI for Spring Boot 2 and Spring Boot 3

The branch for the Spring Boot 3.x version is available here: https://github.com/marco76/java-angular-basic/tree/external-api-open-api-spring-boot-3

The branch (tag) for the Spring Boot 2.x version is available here: https://github.com/marco76/java-angular-basic/tree/v0.0.1-Angular12_Spring2_OpenAPI

At the end of the post you have a list of changes required to migrate OpenAPI from Spring Boot 2 to Spring Boot 3.

Test the application

You can run the project with :
mvn clean package and java -jar ./backend/backend-0.1.1-SNAPSHOT.jar.

localhost:8080 should show the standard angular application, localhost:8080/swagger-ui/index.html should show the OpenAPI definition.

Architecture of the project

In our existing project we add a new module open-api

openapi folder structure

The backend project will reference the API generated by this project:

<!-- pom.xml (backend) --> 
<dependency> 
  <groupId>${project.groupId}</groupId> 
  <artifactId>open-api</artifactId> 
  <version>${project.version}</version> 
</dependency> 

The main pom needs to reference the new module too.

<!-- parent pom.xml --> 
<modules> 
  <module>open-api</module> 
  <module>frontend</module> 
  <module>backend</module> 
</modules> 

The OpenApi Spring Boot module

This module is pretty small, it contains only the specifications of the API.

In our example we will generate the code directly in this module. Some developers prefer to include only the specification and generate the code directly in the consumer module.

swagger configuration files in folder

In the api pom.xml we need the following dependencies

<dependencies> 
    <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> 
 
    <dependency> 
        <groupId>org.openapitools</groupId> 
        <artifactId>jackson-databind-nullable</artifactId> 
        <version>0.2.6</version> 
    </dependency> 
    <dependency> 
        <groupId>io.swagger.core.v3</groupId> 
        <artifactId>swagger-annotations</artifactId> 
        <version>2.2.15</version> 
    </dependency> 
</dependencies> 

You should check the most appropriate version for your project.

The work is done by the openapi generator plugin.

<!--https://openapi-generator.tech/docs/plugins/--> 
<plugin> 
  <groupId>org.openapitools</groupId> 
  <artifactId>openapi-generator-maven-plugin</artifactId> 
  <version>7.2.0</version> <!-- important for Spring Boot 3.x --> 
  <executions> 
    <execution> 
      <goals> 
        <goal>generate</goal> 
      </goals> 
      <id>buildApi</id> 
      <configuration> 
 
      <!-- path to the specification --> 
	   <inputSpec>${basedir}/src/main/resources/java-angular-basic.yaml</inputSpec> 
 
      <!--https://openapi-generator.tech/docs/generators/spring --> 
      <generatorName>spring</generatorName> 
      <library>spring-boot</library> 
 
      <modelNameSuffix>${swagger.modelNameSuffix}</modelNameSuffix> 
      <generateApis>true</generateApis> 
      <generateModels>true</generateModels> 
      
	   <!-- ... lot of parameters and configuration omitted here, look in the original file ... --> 
 
         <!-- configuration --> 
         <configOptions> 
           <interfaceOnly>true</interfaceOnly> 
           <useBeanValidation>true</useBeanValidation> 
           <performBeanValidation>true</performBeanValidation> 
           <modelPackage>${swagger.modelPackage}</modelPackage> 
           <apiPackage>${swagger.basePackage}.controller</apiPackage> 
           <sourceFolder>/src/main/java</sourceFolder> 
           <implFolder>/src/main/java</implFolder> 
           <serializableModel>true</serializableModel> 
           <useJakartaEe>true</useJakartaEe> <!-- important for Spring Boot 3.x --> 
           <useSpringBoot3>true</useSpringBoot3> <!-- important for Spring Boot 3.x --> 
        </configOptions> 
      </configuration> 
    </execution> 
  </executions> 
</plugin> 

There are a lot of options configured, what is more important for us at this moment are the following lines:

<!-- path to the specification --> 
<inputSpec>${basedir}/src/main/resources/java-angular-basic.yaml</inputSpec> 
 
<!--https://openapi-generator.tech/docs/generators/spring --> 
<generatorName>spring</generatorName> 
<library>spring-boot</library> 

<inputSpec> defines where is the specification file in our folder structure.

<generatorName> and <library> use a generator created specifically for Spring Boot that will allow us to generate directly @RestControllers from the specifications.

Specifications OpenApi 3

This is the content of our specifications file:

openapi: 3.0.3 # version of the specification 
info: 
  version: '1' 
  title: marmo.dev Angular Spring Boot Example Api 
 
servers: 
  - url: http://localhost:8080 
 
paths: 
  /hello-open: 
    get: 
      summary: return a simple generic greeting 
      operationId: getGreeting 
      responses: 
        200: 
          description: General greeting 
          content: 
            application/json: 
              schema: 
                $ref: '#/components/schemas/Greeting' 
  /hello-open/{name}: 
    parameters: 
      - in: path 
        name: name 
        schema: 
          type: string 
        required: true 
        description: "Name" 
        example: "Marco" 
    get: 
      description: return a greeting with name 
      operationId: getPersonalGreeting 
      responses: 
        200: 
          description: Personal greeting 
          content: 
            application/json: 
              schema: 
                $ref: '#/components/schemas/Greeting' 
components: 
  schemas: 
    Greeting: 
      type: object 
      properties: 
        message: 
          type: string 
          example: 'Hello from Spring' 
          default: 'Hello visitor' 

You can create your own and or test it on https://editor.swagger.io/.

We have two simple GET Responses and we have a Schema (represented in Java by a class) that will contain the message to send to the client.

If you open the browser at localhost:8080/swagger-ui/index.html you should see the OpenAPI definition.

swagger api UI

When we build the project with maven, the Java classes are generated.

swagger api UI

Our project includes an interface with the GET methods and a model with the class that will define the format of our answer. The ApiUtil is an helper class that contains some features not generated to h

Here is an example of the generated GET for /hello-open :

@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen") 
@Validated 
@Api(value = "hello-open", description = "the hello-open API") 
public interface HelloOpenApi { 
 
    default Optional<NativeWebRequest> getRequest() { 
        return Optional.empty(); 
    } 
 
    /** 
     * GET /hello-open : return a simple generic greeting 
     * 
     * @return General greeting (status code 200) 
     */ 
    @ApiOperation(value = "return a simple generic greeting", nickname = "getGreeting", notes = "", response = Greeting.class, tags={  }) 
    @ApiResponses(value = {  
        @ApiResponse(code = 200, message = "General greeting", response = Greeting.class) }) 
    @GetMapping( 
        value = "/hello-open", 
        produces = { "application/json" } 
    ) 
    default ResponseEntity<Greeting> getGreeting() { 
        getRequest().ifPresent(request -> { 
            for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { 
                if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { 
                    String exampleString = "{ \"message\" : \"Hello from Spring\" }"; 
                    ApiUtil.setExampleResponse(request, "application/json", exampleString); 
                    break; 
                } 
            } 
        }); 
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); 
    } 

There is a lot of stuff inside this class. What is relevant for us is that an interface containing some REST responses are generated and ready to be used.

On the base of the Greeting schema we have Greeting.java . Here we show only partially the content of the code. Interesting for us are the dependencies (Jackson, Hibernate, Swagger) used by the generator.

import java.util.Objects; 
import com.fasterxml.jackson.annotation.JsonProperty; 
import com.fasterxml.jackson.annotation.JsonCreator; 
import io.swagger.annotations.ApiModel; 
import io.swagger.annotations.ApiModelProperty; 
import org.openapitools.jackson.nullable.JsonNullable; 
import java.io.Serializable; 
import javax.validation.Valid; 
import javax.validation.constraints.*; 
import org.hibernate.validator.constraints.*; 
 
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen") 
public class Greeting  implements Serializable { 
  private static final long serialVersionUID = 1L; 
 
  @JsonProperty("message") 
  private String message = "Hello visitor"; 
 
  public Greeting message(String message) { 
    this.message = message; 
    return this; 
  } 
 
  @ApiModelProperty(example = "Hello from Spring", value = "") 
 
  public String getMessage() { 
    return message; 
  } 
 
  public void setMessage(String message) { 
    this.message = message; 
  } 

The code is complete overriding some standard methods:

java greeting result

The implementation in Spring Boot

In our main project (backend) we can implement the interfaces created, be sure that the package with the API is imported in your project and updated.

import dev.marco.example.api.controller.HelloOpenApi; 
import dev.marco.example.api.model.Greeting; 
import org.springframework.http.ResponseEntity; 
import org.springframework.web.bind.annotation.RestController; 
 
@RestController 
public class HelloControllerApiImpl implements HelloOpenApi { 
  @Override 
  public ResponseEntity<Greeting> getGreeting() { 
    Greeting greeting = new Greeting(); 
    greeting.message("Hello from Spring Boot"); 
    return ResponseEntity.ok(greeting); 
  } 
 
  @Override 
  public ResponseEntity<Greeting> getPersonalGreeting(String name) { 
      Greeting greeting = new Greeting(); 
      greeting.message("Hello " + name + ", enjoy Spring Boot"); 
      return ResponseEntity.ok(greeting); 
  } 
} 

Here the complete implementation of our 2 GET methods. OpenApi (Swagger) manages the boilerplate code and our task is limited to the implementation.

Result

Here you can find an image of the result when we call our method (getPersonalGreeting) from the browser

rest json answer in browser

OpenAPI migration from Spring Boot 2.x to Spring Boot 3.x

There are relevant changes between the 2 versions, here a summary.

SpringFox replaced with SpringDoc OpenAPI

We removed SpringFox for the removal of the specifications to use springdoc-openapi-starter-webmvc-ui in the main project.

You can find the migration documentation here: https://springdoc.org/migrating-from-springfox.html

Libraries changes

We replaced Hibernate validator to use a Spring dependency:

From org.hibernate:hibernate-validator:7.0.1.Final to org.springframework.boot:spring-boot-starter-validation.

We added a dependency to org.springframework.boot:spring-boot-starter-web (we removed it from the root pom).

We added a dependency io.swagger.core.v3:swagger-annotations:2.2.15.

package javax.validation does not exist: javax.annotation and javax.validation errors

There are multiple issues open in the OpenAPI project with this subject. A simple migration from Spring Boot 2.x to Spring Boot 3.x changes the dependencies to some javax libraries that have been renamed to jakarta.

The exception is :

You can find some debates around this issue here (still open at the end of 2023):

GitHub Issue 14143: Support SpringBoot 3.0 GA
GitHub Issue 141696: RestTemplate support for Spring boot 3

To solve the issue whe can tell the generator (goal generate in maven) that it has to use JakartaEE and Spring Boot 3:

... 
<configOptions> 
    <useJakartaEe>true</useJakartaEe> 
    <useSpringBoot3>true</useSpringBoot3> 
</configOptions> 

To use these properties the openapi-generator has to be updated to a recent version (>= 6.3.0)


You could be interested in

logo of the category: spring-big.svg Spring Boot: REST controller Test example

How to test the @RestController with Spring Boot
2017-10-01

logo of the category: spring-big.svg How to deploy a Java and Angular webapp in one JAR/WAR

How to configure a fullstack Angular and Spring Boot application
2018-04-26
WebApp built by Marco using SpringBoot 3.2.4 and Java 21, in a Server in Switzerland without 'Cloud'.