Interactive REST API Documentation with Swagger UI

Bing Liu Microservices, Spring, Spring Boot, Technology Snapshot Leave a Comment

I am assisting a client that is migrating from a monolithic legacy application to a modern Microservice stack with Spring REST. We are helping to implement Swagger UI to provide both a front-end API UI, as well as to provide a level of documentation at the same time. This implementation has simultaneously met our project requirements, as well as garnering some positive feedback from our client!

Swagger UI is one of the most popular tools to visually render beautiful, interactive API documentation. In this blog, I’ll use a REST API application to demonstrate some usage of Swagger UI. The source project is available on GitHub.

Configuring Swagger 2 and Swagger UI in the Application

Our first steps will be to integrate Swagger and Swagger UI into an existing Spring Boot REST application. Our reference application uses the latest version available on the Maven Repository.

1. Add the Swagger and Swagger UI Maven Dependency in the pom.xml.
	<dependency>
<groupId>io.springfox</groupId>
	       <artifactId>springfox-swagger2</artifactId>
		 <version>2.9.2</version>
	</dependency>

	<dependency>
		<groupId>io.springfox</groupId>
		<artifactId>springfox-swagger-ui</artifactId>
		<version>2.9.2</version>
</dependency>
2. Configure Swagger

Swagger 2 is enabled through the @EnableSwagger2 annotation. The bean definition (below) of the Springfox Docket Bean provides the primary API configuration with sensible defaults and convenient methods for configuration. For detail usage of these methods, refer to https://swagger.io/tools/swagger-ui/

@Configuration
@EnableSwagger2
public class SwaggerConfig {
 @Bean
 public Docket docket() {

  return new Docket(DocumentationType.SWAGGER_2)
   .apiInfo(apiInfo())
   .select()
   .apis(RequestHandlerSelectors.basePackage("com.keyhole.demo.swagger.web"))
   .build();
 }

 private ApiInfo apiInfo() {
  return new ApiInfoBuilder()
   .title("Jira Task Assignment Services")
   .description("A REST service for task assignments.")
   .licenseUrl("")
   .termsOfServiceUrl("")
   .version("1.0.0")
   .license("")
   .contact(new Contact("Keyhole Software",
    "keyholesoftware.com",
    "asktheteam@keyholesoftware.com"))
   .build();
 }
}

3. Verification

Now we must perform verification to ensure that everything is working. Please type the following URL in your browser to verify that Swagger is working: http://localhost:8080/v2/api-docs

The result is a JSON response with many key-value pairs. These are not easy to read. This is the reason Swagger UI comes to play.

To verify Swagger UI, now enter this URL in your browser: http://locahost:8080/swagger-ui.html.

The result should look like this:

Exploring Swagger UI

As mentioned, Swagger UI is a tool used to generate an interactive API console for users to learn and try an API.

See Also:  What's New in JUnit 5.1

There three major Swagger annotations for the controller to integrate:

@Api: The description of the general responsibility of the controller.
@ApiOperation: The responsibility of the specific method.
@ApiParam: The parameter the method is expecting and whether it is mandatory or not.

For example:

@RestController
@Api(value="Agile Jira Tasks Services")
@RequestMapping(value="jiraboard")
public class JiraTaskController {
    
    private JiraTaskRepository jiraTaskRepository;
    private DeveloperRepository developerRepository;
    public Iterable<Developer> findAllDevelopers()
    public Optional<Developer> findDeveloperById(@ApiParam(value = "Input a whole number as Developer ID")@PathVariable("id") Long id)
    public Iterable<JiraTask> findAllJiraTasks() 
    public Iterable<JiraTask> findJiraTasksBlockerStatus(@ApiParam(value = "Choose true/false from dropdown") @PathVariable("isBlocked") Boolean isBlocked) 
    
    @PostMapping("/addJiraTask")
    @ApiOperation(value="Add a new Jira task")
    public void addNewJiraTask(
        @ApiParam(value = "A New Task ID") @RequestParam(required = true) Integer taskId,
        @ApiParam(value = "A New Task Name") @RequestParam(required = true) String taskName,
        @ApiParam(value = "A New Task Status") @RequestParam(required = true) String status,
        @ApiParam(value = "A New Task Due Date in ISO Format") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @RequestParam LocalDate dueDate,
        @ApiParam(value = "If This Task Blocked") @RequestParam(required = true) Boolean blocker)
    {
        JiraTask task = new JiraTask();
        task.setTaskId(taskId);
        task.setTaskName(taskName);
        task.setStatus(status);
        task.setDueDate(dueDate);
        task.setBlocker(blocker);
        
        jiraTaskRepository.save(task);
    }
}


This Swagger UI page lists all endpoints defined in the REST controller. It specifies each URL, HTTP method, and description.

1. Make a Get Request

In the JiraTaskController, there is an endpoint /developer/{id} defined.

	@GetMapping("/developer/{id}")
	@ApiOperation(value = "Returns a developer by Id")
	@ApiResponses({
	 @ApiResponse(code = 400, message = "Invalid request parameters supplied"),
	 @ApiResponse(code = 404, message = "Requested data not found")
	})
	public Optional < Developer > findDeveloperById(@ApiParam(value = "Input a whole number as Developer ID", example = "1") @PathVariable("id") Integer id) {

	 return developerRepository.findById(id);
	}

There is a corresponding endpoint in the Swagger UI. Expand the /jiraboard/developer/{id} endpoint, enter developer ID, and then click the “Try it out!” button to display. The result displays in JSON format in the Response Body. Swagger UI also shows the cURL and Request URL.

 

2. Make a Post Request

This is the beauty part of Swagger UI. Its API’s resources are tightly integrated into the service code without any implementation logic needed, thereby maintaining the synchronization in APIs and its documentation.

	@PostMapping("/addJiraTask")
	@ApiOperation(value = "Add a new Jira task")
	@ApiResponses({ @ApiResponse(code = 400, message = "Invalid request parameters supplied"),
                    @ApiResponse(code = 404, message = "Requested data not found") })
	public void addNewJiraTask(
			@ApiParam(value = "A New Task ID", example = "1") @RequestParam(required = true) Integer taskId,
			@ApiParam(value = "A New Task Name", example = "taskName") @RequestParam(required = true) String taskName,
			@ApiParam(value = "A New Task Status", example = "status") @RequestParam(required = true) String status,
			@ApiParam(value = "A New Task Due Date in ISO Format", example = "2018-10-12") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @RequestParam LocalDate dueDate,
			@ApiParam(value = "If this Task Blocked", example = "true") @RequestParam(required = true) Boolean blocker) {
		JiraTask task = new JiraTask();
		task.setTaskId(taskId);
		task.setTaskName(taskName);
		task.setStatus(status);
		task.setDueDate(dueDate);
		task.setBlocker(blocker);

		jiraTaskRepository.save(task);
	}

Click endpoint /jiraboard/addJiraTask and it expands to display this page.

See Also:  Encrypting Working Files Locally in Spring Batch

Result page also includes the Curl and Request URL.

Note: I found one “gotcha” in swagger 2 latest version 2.9.2 (It was not an issue in 2.8.0 version). I have provided a workaround:

The @ApiParam has an issue where, without a default value set in “example,” the attribute will cause this exception.
:java.lang.NumberFormatException:For input integer:. You must provide some integer to placate the configuration.

	@DeleteMapping("/deleteTask/{id}")
	@ApiOperation(value = "Delete a task")
	public void deleteTask(@ApiParam(value = "Task ID to delete", example = "1") @PathVariable("id") Integer id) {
		jiraTaskRepository.deleteById(id);
	} 

Final Thoughts

Swagger UI does provide end developers a way to interact and explore your API endpoints effortlessly!

However, there are a couple of limitations I experienced. In case of a long description needed, Swagger UI doesn’t provide a link out from the description to a separated page to display it. Another one is that it is not easy to customize Swagger UI with your company branding.

What Do You Think?