Documenting a Spring REST API Using OpenAPI 3.0

1. Overview

Documentation is an essential part of building REST APIs. In this tutorial, we’ll take a look at SpringDoc — a tool that simplifies the generation and maintenance of API docs, based on the OpenAPI 3 specification, for Spring Boot 1.x and 2.x applications.

2. Setting up springdoc-openapi

To have springdoc-openapi automatically generate the OpenAPI 3 specification docs for our API, we simply add the springdoc-openapi-core dependency to our pom.xml:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-core</artifactId>
    <version>1.1.44</version>
</dependency>

Then, when we run our application, the docs will be available at the path /v3/api-docs by default — for example:

http://localhost:8080/v3/api-docs/

To use a custom path, we can indicate in the application.properties file:

springdoc.api-docs.path=/api-docs

Now, we’ll be able to access the docs at:

http://localhost:8080/api-docs/

The OpenAPI definitions are in JSON _ format by default. For _yaml format, we can obtain the definitions at:

http://localhost:8080/api-docs.yaml

3. Setting up springdoc-openapi with Swagger UI

Besides generating the OpenAPI 3 specification itself, we can integrate springdoc-openapi with Swagger UI so that we can interact with our API specification and exercise the endpoints.

3.1. Maven Dependency

All we have to do to set up springdoc-openapi with Swagger UI is to add the dependency springdoc-openapi-ui to the project’s pom.xml:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.1.44</version>
</dependency>

We can now access the API documentation at:

http://localhost:8080/swagger-ui.html

We could customize the path by adding the property:

springdoc.swagger-ui.path=/swagger-ui-custom.html

3.2. Sample API

Suppose our application has a controller for managing Books:

@RestController
@RequestMapping("/api/book")
public class BookController {

    @Autowired
    private BookRepository repository;

    @GetMapping("/{id}")
    public Book findById(@PathVariable long id) {
        return repository.findById(id)
            .orElseThrow(() -> new BookNotFoundException());
    }

    @GetMapping("/")
    public Collection<Book> findBooks() {
        return repository.getBooks();
    }

    @PutMapping("/{id}")
    @ResponseStatus(HttpStatus.OK)
    public Book updateBook(@PathVariable("id") final String id, @RequestBody final Book book) {
        return book;
    }
}

Then, when we run our application, we can view the documentation at:

http://localhost:8080/swagger-ui-custom.html

image

Let’s drill down to the /api/book endpoint and see the details for its request and response:
image

4. Integrating springdoc-openapi with Spring WebFlux

We can integrate springdoc-openapi and Swagger UI in a Spring WebFlux project by adding springdoc-openapi-webflux-ui:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-webflux-ui</artifactId>
    <version>1.1.44</version>
</dependency>

And, as before, the docs will be accessible at:

http://localhost:8080/swagger-ui.html

Here again, we could add the springdoc.swagger-ui.path property in the application.properties file to customize the path.

5. Using springdoc-openapi Maven Plugin

The springdoc-openapi library provides a Maven plugin springdoc-openapi-maven-plugin for generating OpenAPI descriptions in json and yaml formats.

The springdoc-openapi-maven-plugin plugin works with the spring-boot-maven plugin. Maven runs the openapi plugin during the integration-test phase.

Let’s see how we can configure the plugin in our pom.xml:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.1.8.RELEASE</version>
    <executions>
        <execution>
            <id>pre-integration-test</id>
            <goals>
                <goal>start</goal>
            </goals>
        </execution>
        <execution>
            <id>post-integration-test</id>
            <goals>
                <goal>stop</goal>
            </goals>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-maven-plugin</artifactId>
    <version>0.2</version>
    <executions>
        <execution>
            <phase>integration-test</phase>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Also, we can configure the plugin to use custom values:

<plugin>
    <executions>
        .........
    </executions>
    <configuration>
        <apiDocsUrl>http://localhost:8080/v3/api-docs</apiDocsUrl>
        <outputFileName>openapi.json</outputFileName>
        <outputDir>${project.build.directory}</outputDir>
    </configuration>
</plugin>

Let’s take a closer look at the parameters that we can configure for the plugin:

  • apiDocsUrl – URL where the docs can be accessed in json format, with a default of http://localhost:8080/v3/api-docs

  • outputFileName – Name of the file where the definitions are stored, defaults to openapi.json

  • outputDir – Absolute path for the directory where the docs are stored – by default, $\{project.build.directory}

6. Automatic Document Generation Using JSR-303 Bean Validation

When our model includes JSR-303 bean validation annotations, such as @NotNull, @NotBlank, @Size, @Min, and @Max, the springdoc-openapi library uses them to generate additional schema documentation for the corresponding constraints.

Let’s see an example using our Book bean:

public class Book {

    private long id;

    @NotBlank
    @Size(min = 0, max = 20)
    private String title;

    @NotBlank
    @Size(min = 0, max = 30)
    private String author;

}

Now, the documentation generated for the Book bean is a little more informative:
image

7. Generate Documentation Using @ControllerAdvice and @ResponseStatus

Using @ResponseStatus on methods in a @RestControllerAdvice class will automatically generate documentation for the response codes. In this @RestControllerAdvice class, the two methods are annotated with @ResponseStatus:

@RestControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(ConversionFailedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<String> handleConnversion(RuntimeException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(BookNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResponseEntity<String> handleBookNotFound(RuntimeException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

And, we’ll now see documentation for the response codes 400 and 404:
image

8. Conclusion

In this article, we learned to set up springdoc-openapi in our projects. Then, we saw how to integrate springdoc-openapi with the Swagger UI. We also saw how to do this with Spring Webflux projects.

Next, we used the springdoc-openapi Maven Plugin to generate OpenAPI definitions for our APIs. Finally, we looked at how springdoc-openapi generates documentation automatically using JSR 303 bean validation annotations and the @ResponseStatus annotations in @ControllerAdvice class.

The springdoc-openapi generates API documentation as per OpenAPI 3 specification. It handles the Swagger UI configuration for us, making API document generation a fairly simple task.

As always, the code is available over on GitHub.