Spring Boot OpenAPI generator example


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.

The code of this post is in this branch Open API example with Spring Boot

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

Image.png

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.

Image.png

In the api pom.xml we need the following dependencies

<dependencies> 
  <dependency> 
    <groupId>io.springfox</groupId> 
    <artifactId>springfox-boot-starter</artifactId> 
    <version>3.0.0</version> 
  </dependency> 
 
  <dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-validator</artifactId> 
    <version>7.0.1.Final</version> 
  </dependency> 
 
  <dependency> 
    <groupId>org.openapitools</groupId> 
    <artifactId>jackson-databind-nullable</artifactId> 
    <version>0.2.0</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>5.1.0</version> 
  <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> 
        </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: Marco.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.

Swagger UI image

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

Image.png

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:

Image.png

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

Image.png


You could be interested in

Spring Boot: REST controller Test example

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

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, Java 17, Mustache, Markdown and in Azure