Spring Microservices RESTFul API Documentation With Swagger Part 1

When back-end APIs are developed and exposed to the client, having good documentation is key. API documentation should be properly structured so that it provides all information related to the API. Without having proper documentation, the client using the API will struggle to access it. If this happens, the API is useless. There are several tools to document the APIs/RESTful Services. However, when you have developed a RESTful service using Spring Boot, then Swagger2 is the obvious choice for documentation because it makes your documentation very easy. 

  1. Swagger is the oldest and most mature spec. 
  2. Supports most languages.
  3. Easy to get started with.
  4. Good community support. 
  5. Has a Maven plugin and is easily configurable with a Spring Boot application.

In this and subsequent articles, we will see how can Swagger2 be configured with a Spring Boot RESTful service. In Part 1, I will show you how to configure your application without security and in Part 2, we'll add security.

Configuration Without Security

The following steps need to be performed to include Swagger in your Spring Boot application and document the API. I will do it for a fresh project 

Step 1 — Create the Project Structure and create artifacts in the correct location.

Creating initial project structure

Step 2 — Add the following Swagger dependencies in your pom.xml file.

Adding dependencies in pom.xmlComplete pom.xml

XML
 




xxxxxxxxxx
1
64


1
<?xml version="1.0" encoding="UTF-8"?>
2
<project xmlns="http://maven.apache.org/POM/4.0.0"
3
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5
 <modelVersion>4.0.0</modelVersion>
6
 <parent>
7
  <groupId>org.springframework.boot</groupId>
8
  <artifactId>spring-boot-starter-parent</artifactId>
9
  <version>2.2.6.RELEASE</version>
10
  <relativePath ></relativePath> <!-- lookup parent from repository -->
11
 </parent>
12
 <groupId>com.demo</groupId>
13
 <artifactId>swagger-config-demo</artifactId>
14
 <version>0.0.1-SNAPSHOT</version>
15
 <name>swagger-config-demo</name>
16
 <description>Swagger Demo</description>
17
 
          
18
 <properties>
19
  <java.version>1.8</java.version>
20
 </properties>
21
 
          
22
 <dependencies>
23
  <dependency>
24
   <groupId>org.springframework.boot</groupId>
25
   <artifactId>spring-boot-starter-web</artifactId>
26
  </dependency>
27
 
          
28
  <dependency>
29
   <groupId>io.springfox</groupId>
30
   <artifactId>springfox-swagger-ui</artifactId>
31
   <version>2.6.1</version>
32
 
          
33
  </dependency>
34
  <dependency>
35
   <groupId>io.springfox</groupId>
36
   <artifactId>springfox-swagger2</artifactId>
37
   <version>2.6.1</version>
38
  </dependency>
39
  
40
 
          
41
  <dependency>
42
   <groupId>org.springframework.boot</groupId>
43
   <artifactId>spring-boot-starter-test</artifactId>
44
   <scope>test</scope>
45
   <exclusions>
46
    <exclusion>
47
     <groupId>org.junit.vintage</groupId>
48
     <artifactId>junit-vintage-engine</artifactId>
49
    </exclusion>
50
   </exclusions>
51
  </dependency>
52
 </dependencies>
53
 
          
54
 <build>
55
  <plugins>
56
   <plugin>
57
    <groupId>org.springframework.boot</groupId>
58
    <artifactId>spring-boot-maven-plugin</artifactId>
59
   </plugin>
60
  </plugins>
61
 </build>
62
 
          
63
</project>
63
</project>



Step 3 — Configuring Swagger in the application. To configure Swagger in the application, we need to create a config file, which will have all Swagger related configurations. I will explain each after this code snippet.

Java
 




xxxxxxxxxx
1
63


1
package com.demo.config;
2
 
          
3
import java.util.ArrayList;
4
import java.util.HashSet;
5
import java.util.List;
6
import java.util.Set;
7
 
          
8
import org.springframework.context.annotation.Bean;
9
import org.springframework.context.annotation.Configuration;
10
import org.springframework.http.ResponseEntity;
11
import org.springframework.web.bind.annotation.RequestMethod;
12
import org.springframework.web.bind.annotation.RestController;
13
 
          
14
import springfox.documentation.builders.PathSelectors;
15
import springfox.documentation.builders.RequestHandlerSelectors;
16
import springfox.documentation.builders.ResponseMessageBuilder;
17
import springfox.documentation.schema.ModelRef;
18
import springfox.documentation.service.ApiInfo;
19
import springfox.documentation.spi.DocumentationType;
20
import springfox.documentation.spring.web.plugins.Docket;
21
import springfox.documentation.swagger2.annotations.EnableSwagger2;
22
 
          
23
@Configuration
24
@EnableSwagger2
25
public class SwaggerConfig {
26
 @Bean
27
    public Docket productApi() {
28
     Set<String> responseProduceType = new HashSet<String>();
29
     responseProduceType.add("application/json");
30
     responseProduceType.add("application/xml");
31
  return new Docket(DocumentationType.SWAGGER_2)
32
                .select().apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
33
                .paths(PathSelectors.any()).build()
34
                .useDefaultResponseMessages(false)
35
                .genericModelSubstitutes(ResponseEntity.class)
36
                .produces(responseProduceType)
37
                .consumes(responseProduceType)
38
                
39
                .apiInfo(apiInfo());
40
                
41
                
42
    }
43
 
          
44
 private ApiInfo apiInfo() {
45
  @SuppressWarnings("deprecation")
46
  ApiInfo apiInfo = new ApiInfo(
47
    "Item REST API",
48
    "All Item related information",
49
    "API",
50
    "Terms of services",
51
    "nitesh841@gmail.com",
52
    "License of API",
53
    "API License URL");
54
  return apiInfo;
55
  
56
 }
57
 
          
58
 private Object apiKey() {
59
  // TODO Auto-generated method stub
60
  return null;
61
 }
62
}



In above code snippet, you can see following configuration needs to be done:

Now, you can see there are few customizations that I had done like:

There are a few more customization that can be performed on top of the Docket object.

Step 4 - Once Swagger Configurations are done, Controllers and Model objects need to be documented using available annotations. 

1. Annotations of End Points 

Any Class having the @RestController annotation will be considered as REST API Controllers (Resource).

Java
 




xxxxxxxxxx
1
79


 
1
package com.demo.resource;
2
 
          
3
 
          
4
import java.util.List;
5
 
          
6
import org.springframework.beans.factory.annotation.Autowired;
7
import org.springframework.http.HttpStatus;
8
import org.springframework.http.ResponseEntity;
9
import org.springframework.web.bind.annotation.DeleteMapping;
10
import org.springframework.web.bind.annotation.GetMapping;
11
import org.springframework.web.bind.annotation.PatchMapping;
12
import org.springframework.web.bind.annotation.PathVariable;
13
import org.springframework.web.bind.annotation.PostMapping;
14
import org.springframework.web.bind.annotation.PutMapping;
15
import org.springframework.web.bind.annotation.RequestBody;
16
import org.springframework.web.bind.annotation.RestController;
17
 
          
18
import com.demo.model.ItemDto;
19
import com.demo.service.ItemService;
20
 
          
21
import io.swagger.annotations.Api;
22
import io.swagger.annotations.ApiOperation;
23
import io.swagger.annotations.ApiResponse;
24
import io.swagger.annotations.ApiResponses;
25
@RestController
26
@Api(value = "Item Resource to handle all Item related action and queries ")
27
public class ItemResource {
28
 
          
29
 @Autowired
30
 private ItemService service;
31
 
32
 @GetMapping("/items")
33
 @ApiOperation(value = "Return all Items available in the System", response = List.class)
34
 @ApiResponses(value = {
35
         @ApiResponse(code = 200, message = "Successfully retrieved list"),
36
         @ApiResponse(code = 401, message = "You are not authorized to view the resource"),
37
         @ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
38
         @ApiResponse(code = 404, message = "Requested Resource Not Found")
39
 })
40
 public ResponseEntity<List<ItemDto>> fetchItems(){
41
  List<ItemDto> items = service.fetchAll();
42
  return new ResponseEntity<List<ItemDto>>(items, HttpStatus.OK);
43
 }
44
 
45
 @ApiOperation(value = "Return Item for provided Id", response = ItemDto.class)
46
 @GetMapping("/items/{id}")
47
 public ResponseEntity<ItemDto> fetchItemById(@PathVariable long id){
48
  ItemDto ItemDto = service.fetchById(id);
49
  return new ResponseEntity<ItemDto>(ItemDto, HttpStatus.OK);
50
 }
51
 
52
 @PostMapping("/items")
53
 @ApiOperation(value = "Create the Item based on input provided", response = ItemDto.class)
54
 public ResponseEntity<ItemDto> create(@RequestBody ItemDto dto){
55
  ItemDto ItemDto = service.create(dto);
56
  return new ResponseEntity<ItemDto>(ItemDto, HttpStatus.OK);
57
 }
58
 
59
 @ApiOperation(value = "Updated Item for provided Id and with updated details", response = ItemDto.class)
60
 @PutMapping("/items/{id}")
61
 public ResponseEntity<ItemDto> update(@PathVariable Long id, @RequestBody ItemDto dto){
62
  ItemDto itemDto = service.update(dto);
63
  return new ResponseEntity<ItemDto>(itemDto, HttpStatus.OK);
64
 }
65
 
66
 @ApiOperation(value = "Approves the Item for provided Id", response = ItemDto.class)
67
 @PatchMapping("/items/{id}")
68
 public ResponseEntity<ItemDto> approve(@PathVariable Long id){
69
  return null;
70
 }
71
 
72
 @ApiOperation(value = "Delete the Item from System for provided Id", response = ItemDto.class)
73
 @DeleteMapping("/items/{id}")
74
 public ResponseEntity<String> delete(@PathVariable Long id){
75
  service.delete(id);
76
  return new ResponseEntity<String>("Item has been deleted", HttpStatus.OK);
77
 }
78
}



In this Controller/Resource, you can see many annotations. Let's go one by one and see what they are responsible for:

RestController example

GetMapping and API annotations

Service Class Implementation

Java
 




xxxxxxxxxx
1
42


 
1
package com.demo.service;
2
 
          
3
import java.util.ArrayList;
4
import java.util.List;
5
import java.util.stream.Collectors;
6
 
          
7
import org.springframework.stereotype.Service;
8
 
          
9
import com.demo.model.ItemDto;
10
 
          
11
@Service
12
public class ItemService {
13
 List<ItemDto> items = new ArrayList<>();
14
 
15
 public List<ItemDto> fetchAll(){
16
  return items;
17
 }
18
 public ItemDto fetchById(long id) {
19
  return items.stream().filter(dto -> dto.getId() == id).collect(Collectors.toList()).get(0);
20
 }
21
 public ItemDto create (ItemDto dto) {
22
  items.add(dto);
23
  return dto;
24
 }
25
 
26
 public ItemDto update (ItemDto dto) {
27
  items.add(dto);
28
  items.forEach( dto1 ->{
29
   if(dto1.getId() == dto.getId()) {
30
    dto1.setItemCode(dto.getItemCode());
31
    dto1.setItemDescription(dto.getItemDescription());
32
    dto1.setPrice(dto.getPrice());
33
   }
34
  });
35
  return dto;
36
 }
37
 
38
 public void delete(long id) {
39
  items.removeIf(dto -> dto.getId() == id);
40
 }
41
}


 

Spring Boot application 

Java
 




xxxxxxxxxx
1
14


1
package com.demo;
2
 
          
3
import org.springframework.boot.SpringApplication;
4
import org.springframework.boot.autoconfigure.SpringBootApplication;
5
 
          
6
 
          
7
 
          
8
@SpringBootApplication
9
public class SwaggerDemoApplication {
10
 public static void main(String[] args) {
11
  SpringApplication.run(SwaggerDemoApplication.class, args);
12
 }
13
}



2. Annotations for Models 

@ApiModel and @ApiModelProperty can be used to document the Models used in the application. 

Java
 




xxxxxxxxxx
1
86


 
1
package com.demo.model;
2
 
          
3
import java.io.Serializable;
4
 
          
5
import io.swagger.annotations.ApiModel;
6
import io.swagger.annotations.ApiModelProperty;
7
 
          
8
@ApiModel(value = "Item Details", description = "Item Details for perticular Item")
9
public class ItemDto implements Serializable {
10
 
          
11
 private static final long serialVersionUID = -7844251211618763233L;
12
 @ApiModelProperty(name = "Id", value = "Unique Item Id", dataType = "long", readOnly = true, example = "101")
13
 private long id;
14
 
          
15
 @ApiModelProperty(name = "itemCode", value = "Unique Item Code", dataType = "long", required = true, example = "BOOK001")
16
 private String itemCode;
17
 
          
18
 @ApiModelProperty(name = "ItemName", value = "Item Name", dataType = "String", required = true, example = "Microservices architecture")
19
 private String itemName;
20
 
          
21
 @ApiModelProperty(name = "Item Description", value = "Description of the Item", dataType = "String", example = "Microservices architecture book for building Micro-services")
22
 private String itemDescription;
23
 
          
24
 @ApiModelProperty(name = "Status", value = "Status of the item", dataType = "long", required = true)
25
 private long status;
26
 
          
27
 @ApiModelProperty(name = "Price", value = "Price of the item", dataType = "double", required = true, example = "450.40")
28
 private double price;
29
 
          
30
 public long getId() {
31
  return id;
32
 }
33
 
          
34
 public void setId(long id) {
35
  this.id = id;
36
 }
37
 
          
38
 public String getItemCode() {
39
  return itemCode;
40
 }
41
 
          
42
 public void setItemCode(String itemCode) {
43
  this.itemCode = itemCode;
44
 }
45
 
          
46
 public String getItemName() {
47
  return itemName;
48
 }
49
 
          
50
 public void setItemName(String itemName) {
51
  this.itemName = itemName;
52
 }
53
 
          
54
 public String getItemDescription() {
55
  return itemDescription;
56
 }
57
 
          
58
 public void setItemDescription(String itemDescription) {
59
  this.itemDescription = itemDescription;
60
 }
61
 
          
62
 public long getStatus() {
63
  return status;
64
 }
65
 
          
66
 public void setStatus(long status) {
67
  this.status = status;
68
 }
69
 
          
70
 public double getPrice() {
71
  return price;
72
 }
73
 
          
74
 public void setPrice(double price) {
75
  this.price = price;
76
 }
77
 
          
78
 @Override
79
 public String toString() {
80
  return "ItemDto [id=" + id + ", itemCode=" + itemCode + ", itemName="
81
    + itemName + ", itemDescription=" + itemDescription
82
    + ", status=" + status + ", price=" + price + "]";
83
 }
84
 
          
85
}



Step 5 - Once endpoint and Model documentation are done, start the Spring Boot application and access the following URL: http://localhost:8080/swagger-ui.html.

Once you access this url, Swager main page will get open which will have all your apis listed which you have documented 

Item REST API

In this page, you can see following things:

  1. Your Controller/Resource Customized name.
  2. All end points which was annotated by @ApiOperation and then you can see the description provided by you at right.
  3. API Info provided in Swagger config file.

When you access POST Method of Items for creating Item:

POST method of items for creating an Item

In this screenshot you can see following 

  1. Response Content Type - Set in Swagger Config. You can chose either XML or JSON. 
  2. Example Value - Picked from Model, documented by you. 
  3. Try Out - There will be a Try Out button. Once you click on it, a request will go to the server and perform the required operation:

Sending request to server for required operation

Swagger Documentation for Item Model 

Swagger documentation for Item model

Summary 

Other than API Documentation, Swagger can also be used for defining your API Specs, and then later, you can perform API Validation as well. As it is integrated with your application, you can easily access the services.

I hope this is helpful. In the next article (Part 2), I will show you Swagger Configurations for adding security, so that only valid users can access Swagger documentation.

 

 

 

 

Top