Learnitweb

Documenting a Spring REST API Using OpenAPI 3.0

1. Introduction

We need documentation to understand the code or a program. For example, documentation helps us understand what a method does and what it needs as arguments to perform the operation. Similarly, API documentation can also be generated which will be helpful to provide description, request and response format.

Swagger is the most popular tool for API documentation. In this tutorial, we’ll take a look at SpringDoc — a tool that simplifies the generation and maintenance of API documentation based on the OpenAPI 3 specification for Spring Boot 2.x applications.

2. What is OpenAPI Initiative (OAI) and OpenAPI Specification (OAS)

The OpenAPI Initiative (OAI) was created by a consortium of forward-looking industry experts to standardize how the APIs are described. The goal of OAI is to to create and promote vendor neutral description format for APIs.

As mentioned at the official page of OpenAPI Initiative:

The OAS defines a standard, programming language-agnostic interface description for REST APIs, which allows both humans and computers to discover and understand the capabilities of a service without requiring access to source code, additional documentation, or inspection of network traffic. When properly defined via OAS, a consumer can understand and interact with the remote service with a minimal amount of implementation logic. Similar to what interface descriptions have done for lower-level programming, the OAS removes guesswork in calling a service.

3. springdoc-openapi library

The springdoc-openapi java library helps to automate the generation of API documentation using spring boot projects. This library works by examining annotation, classes and configurations at runtime to infer API semantics. This library can auto generate documentation in JSON/YAML and HTML format APIs.

4. Dependency

To integrate swagger-ui with Spring Boot, include the following dependency in the pom.xml:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.5</version>
</dependency>
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.6.5</version> </dependency>
 <dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-ui</artifactId>
      <version>1.6.5</version>
 </dependency>

If project uses spring-data-rest, add the following dependency in addition to springdoc-openapi-ui. This provides support for spring-boot-starter-data-rest types.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-data-rest</artifactId>
<version>1.6.5</version>
</dependency>
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-data-rest</artifactId> <version>1.6.5</version> </dependency>
 <dependency>
     <groupId>org.springdoc</groupId>
     <artifactId>springdoc-openapi-data-rest</artifactId>
     <version>1.6.5</version>
 </dependency>

If a project uses spring-security, you should add the following dependency, in addition to the springdoc-openapi-ui dependency:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-security</artifactId>
<version>1.6.5</version>
</dependency>
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-security</artifactId> <version>1.6.5</version> </dependency>
   <dependency>
      <groupId>org.springdoc</groupId>
      <artifactId>springdoc-openapi-security</artifactId>
      <version>1.6.5</version>
   </dependency>

Adding dependency is enough to get started and this will automatically deploy swagger-ui to a spring-boot application.

  • The Swagger UI page will be available at http://server:port/context-path/swagger-ui.html.
  • The OpenAPI description will be available at http://server:port/context-path/v3/api-docs in json format.
  • Documentation is available in yaml format at /v3/api-docs.yaml.

Here, server is the name of server or its IP. port is the server port. context-path is the context-path of the application.

5. Example

In this example, we’ll create a Spring Boot application and will add a couple of REST APIs to it. We’ll then add SpringDoc support to the application to create documentation of APIs.

5.1 Create a Spring Boot application

The first step is to create a Spring Boot application support.

Following is the pom.xml in our case:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.learnitweb</groupId>
<artifactId>demoOpenApi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demoOpenApi</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.learnitweb</groupId> <artifactId>demoOpenApi</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demoOpenApi</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.6.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.learnitweb</groupId>
	<artifactId>demoOpenApi</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demoOpenApi</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		
		<dependency>
      		<groupId>org.springdoc</groupId>
      		<artifactId>springdoc-openapi-ui</artifactId>
      		<version>1.6.5</version>
 		</dependency>
 		
    	<dependency>
        	<groupId>org.springframework.boot</groupId>
        	<artifactId>spring-boot-starter-actuator</artifactId>
    	</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

5.2 Add REST APIs

We’ll add test APIs to demonstrate the documentation of APIs.

HelloController.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.learnitweb.demoOpenApi.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import com.learnitweb.demoOpenApi.model.Request;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
@RestController
public class HelloController {
@GetMapping("/message")
public ResponseEntity<?> getTestApi() {
Map<String, String> result = new HashMap();
result.put("message", "Hello world");
return new ResponseEntity(result, HttpStatus.OK);
}
@GetMapping("/message/{id}")
public ResponseEntity<?> getTestApiById(@PathVariable String id) {
// use id here
return new ResponseEntity("test message", HttpStatus.OK);
}
@PostMapping("/message")
public ResponseEntity<?> postTestApi(@RequestBody Request req) {
Map<String, String> result = new HashMap();
result.put("message", "Hello world");
return new ResponseEntity(result, HttpStatus.OK);
}
}
package com.learnitweb.demoOpenApi.controller; import java.util.HashMap; import java.util.Map; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import com.learnitweb.demoOpenApi.model.Request; import io.swagger.v3.oas.annotations.parameters.RequestBody; @RestController public class HelloController { @GetMapping("/message") public ResponseEntity<?> getTestApi() { Map<String, String> result = new HashMap(); result.put("message", "Hello world"); return new ResponseEntity(result, HttpStatus.OK); } @GetMapping("/message/{id}") public ResponseEntity<?> getTestApiById(@PathVariable String id) { // use id here return new ResponseEntity("test message", HttpStatus.OK); } @PostMapping("/message") public ResponseEntity<?> postTestApi(@RequestBody Request req) { Map<String, String> result = new HashMap(); result.put("message", "Hello world"); return new ResponseEntity(result, HttpStatus.OK); } }
package com.learnitweb.demoOpenApi.controller;

import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.learnitweb.demoOpenApi.model.Request;

import io.swagger.v3.oas.annotations.parameters.RequestBody;

@RestController
public class HelloController {
	@GetMapping("/message")
	public ResponseEntity<?> getTestApi() {
		Map<String, String> result = new HashMap();
		result.put("message", "Hello world");
		return new ResponseEntity(result, HttpStatus.OK);
	}

	@GetMapping("/message/{id}")
	public ResponseEntity<?> getTestApiById(@PathVariable String id) {
		// use id here
		return new ResponseEntity("test message", HttpStatus.OK);
	}

	@PostMapping("/message")
	public ResponseEntity<?> postTestApi(@RequestBody Request req) {
		Map<String, String> result = new HashMap();
		result.put("message", "Hello world");
		return new ResponseEntity(result, HttpStatus.OK);
	}
}

Request.java

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.learnitweb.demoOpenApi.model;
import java.util.Date;
public class Request {
String message;
String sender;
Date time;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
}
package com.learnitweb.demoOpenApi.model; import java.util.Date; public class Request { String message; String sender; Date time; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public String getSender() { return sender; } public void setSender(String sender) { this.sender = sender; } public Date getTime() { return time; } public void setTime(Date time) { this.time = time; } }
package com.learnitweb.demoOpenApi.model;

import java.util.Date;

public class Request {
	String message;
	String sender;
	Date time;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getSender() {
		return sender;
	}

	public void setSender(String sender) {
		this.sender = sender;
	}

	public Date getTime() {
		return time;
	}

	public void setTime(Date time) {
		this.time = time;
	}
}

That is it. Adding dependency is enough to integrate swagger-ui with the Spring Boot application.

You can access the swagger documentation in HTML format at http://localhost:8080/swagger-ui/index.html.

Documenting a Spring REST API Using OpenAPI 3.0 example

6. Custom path of the swagger documentation in HTML format

To use a custom path of the swagger documentation in HTML format, define springdoc.swagger-ui.path property in Spring Boot configuration file:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
springdoc.swagger-ui.path=/my-app-swagger-ui.html
springdoc.swagger-ui.path=/my-app-swagger-ui.html
springdoc.swagger-ui.path=/my-app-swagger-ui.html

7. Actuator support

By default, actuator endpoints are not exposed in swagger-ui page. In order to display actuator endpoints in swagger-ui, use the following property in Spring Boot configuration file:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
springdoc.show-actuator=true
springdoc.show-actuator=true
springdoc.show-actuator=true

8. Disabling the springdoc-openapi endpoints

To disable the springdoc-openapi endpoint use the following property:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
springdoc.api-docs.enabled=false
springdoc.api-docs.enabled=false
springdoc.api-docs.enabled=false

9. Selecting the Rest Controllers to include in the documentation

To restrict the generated OpenAPI description using package configuration, use the springdoc.packagesToScan property:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Packages to include
springdoc.packagesToScan=com.firstPackage, com.secondPackage
# Packages to include springdoc.packagesToScan=com.firstPackage, com.secondPackage
# Packages to include
springdoc.packagesToScan=com.firstPackage, com.secondPackage

To restrict the generated OpenAPI description using path configuration:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Paths to include
springdoc.pathsToMatch=/v1, /api/employee/**
# Paths to include springdoc.pathsToMatch=/v1, /api/employee/**
# Paths to include
springdoc.pathsToMatch=/v1, /api/employee/**