Spring Boot – Generate REST API Documentation with Swagger
In this blog post, we will see how to integrate Swagger into Spring Boot application to generate API documentation and REST client.
What is Swagger?
Swagger is a specification for describing REST APIs in a format that’s easy to read, understand, and interact with. It allows you to generate documentation for your API, including path attributes, custom objects, and more. It’s a great way to describe your API, but it’s also a powerful tool that can be used beyond simple documentation generation. For example, you can generate a stub of your API that can be used to make requests against your API. Swagger is now known as the OpenAPI
specification.
Swagger allows us to describe the API properties either using the JSON or YAML metadata.
Swagger also provides a Web UI which transforms the JSON metadata to a nice HTML documentation.
Swagger UI can also be used as a REST client.
Swagger Advantages
Documenting REST API very important from API consumers point of view.
API documentation helps the consumers to understand the Request/Response structure of the REST API and invoke API from their applications without any confusion.
Integrating Swagger with Spring Boot
Adding Swagger Dependencies
To integrate swagger into Spring Boot application, first we need to add the following dependency in pom.xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
Code language: Java (java)
Create Swagger Config File
To enable the swagger , we need to create a configuration file and instantiate a Docket bean
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}
Code language: Java (java)
apis(RequestHandlerSelectors.any())
– this method to used to lists the Request handler’s in API’s documentation
RequestHandlerSelectors.any()
– parameter lists all the request handlers.
paths(PathSelectors.any())
– this method to used to filter the API’s based on the path
PathSelectors.any()
– parameter lists all the path selectors.
Grouping API’s
You can also group API’s ( e.g Public, Admin API’s) using group name like below.
@Bean
public Docket publicApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("dev.fullstackcode.eis.public"))
.paths(PathSelectors.regex("/public.*"))
.build()
.groupName("eis-public")
.apiInfo(getApiInfo());
}
@Bean
public Docket adminApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("dev.fullstackcode.eis.admin"))
.paths(PathSelectors.regex("/admin.*"))
.build()
.groupName("eis-admin")
.apiInfo(getApiInfo());
}
Code language: Java (java)
Accessing the Swagger API Documentation
Now let’s start the application
Accessing the Swagger pages
After starting application, you can access swagger documentation by visiting following URL’s
http://localhost:8080/v2/api-docs
– shows API in JSON format
http://localhost:8080/swagger-ui/index.html
– shows API documentation using HTML
The above screenshot shows the all the Controllers and Models available in the application.
By clicking on the each controller , we can see the path to invoke controllers and operations supported by it.
By clicking on each Model, we can see the model fields and its data types.
Testing REST API
Apart from nicely showing all the documentation of API, swagger also provides interface to test it.
By clicking on the operation/method shows a test page, where you can see the required input and sample response in the page.
Clicking on the Try it out
button enables the form for input and shows the execute button, which you can use to submit the request.
Customizing the Swagger Documentation Page
The above screenshot shows the default implementation of swagger documentation page.
Adding API Info
We can further customize the page to show API information such as company branding, license , contact info and description on the page.
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(getApiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo getApiInfo() {
private ApiInfo getApiInfo() {
return new ApiInfoBuilder()
.title("FullStackCode EIS")
.description("This page lists API of Employee Information System")
.version("1.0")
.license("v2.0")
.licenseUrl("https://www.fullstackcode.dev/licence.html")
.contact(new Contact("Suresh", "https://www.fullstackcode.dev/","[email protected]"))
.build();
}
}
Code language: Java (java)
Restricting the Scope of API Documentation
The default page shows all the controllers and Model on the page. For example basic-error-controller
is part of the springboot framework. We can further restrict the API listing based on the base package and paths.
if you look at the swagger config file, we are using following two method class on Docket
object
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
Code language: Java (java)
RequestHandlerSelectors.any() – passes all request handlers
PathSelectors.any()- passes all the available paths in the application.
By passing base package name to apis()
method we can restrict the documentation to API present in that package
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(getApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("dev.fullstackcode.eis"))
.paths(PathSelectors.any())
.build();
}
Code language: Java (java)
below image shows API present in given base package.
Within base package you can further filter API by their path
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(getApiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("dev.fullstackcode.eis"))
.paths(PathSelectors.ant("/employee/**"))
.build();
}
Code language: Java (java)
Below image shows documentation restricted to employee controller as we passing path /employee
.pathis method filter path based on the ant expression.
Describing Model classes
Swagger provides annotation to describe the Model class and Its fields.
@ApiModel - can be used to describe the Model
@ApiModelProperty - can be used to describe the model properties.
Code language: Java (java)
@ApiModelProperty has accepts few attribute which can be used give information about the property.
value – can provide short description of property
required – marks whether property is required or not
example – you can provide example value of property
position – you can define position in the generated documentation if position is important.
@ApiModel(description="Manages employee object ")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@Entity
@ConditionalNotNull(fields = "salary,email",dependsOn = "hire_date" )
public class Employee implements Serializable {
@ApiModelProperty(value = "Auto generated unique id ",required = true)
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Null(groups = {OnCreate.class})
@NotNull(groups = OnUpdate.class)
private Integer id;
@ApiModelProperty(value = "First name of employee ",required = true,example = "Suresh")
@NotBlank(message = "FirstName should not be blank")
@Size(min = 3, message = "{validation.firstNameSize}")
@ValidName
private String first_name;
@ApiModelProperty(value = "Last name of employee ",required = true,example = "Gadupu")
@NotBlank(message = "LastName should not be blank")
@Size(min = 3,message = "LastName should be at least ${min} chars")
private String last_name;
@ApiModelProperty(value = "Gender of employee ")
@Enumerated(EnumType.STRING)
private Gender gender;
@ApiModelProperty(value = "Birth date of employee ")
@PastOrPresent
private LocalDate birth_date;
@ApiModelProperty(value = "Hiring date of employee ")
@PastOrPresent
private LocalDate hire_date;
// @NotBlank // (message = "Email should not be blank")
@ApiModelProperty(value = "Email of employee ")
@Email // (message = "Not a valid email")
private String email;
@ApiModelProperty(value = "Salary of employee ")
@PositiveOrZero
private BigDecimal salary;
@ApiModelProperty(value = "Department details of employee ")
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
......
}
Code language: Java (java)
example values are get reflected in client also
Describing Controller classes
Similar to the model classes we can swagger provides annotations for describing the controller classes.
@Api - describes the Coontroller - used at class level.
@ApiOperation - desrcibes the operation. used at method level
@ApiParam - describes the parameter passed to the controller methods. Used at param level.
Code language: Java (java)
@Api(tags = "Manages operations on Employee ")
@RestController
@RequestMapping("/employee")
@Validated
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@ApiOperation(value = "getEmployees",notes = "Get list of all employees")
@GetMapping()
public List<Employee> getEmployees() {
return employeeService.getAllEmployees();
}
@ApiOperation(value = "getEmployee",notes = "Get employee base on id")
@GetMapping("/{id}")
public Employee getEmployee( @ApiParam(value = "Id of employee",required = true) @PathVariable @Min(1) Integer id) {
return employeeService.getEmployeeById(id).orElseThrow(() ->new ResponseStatusException(HttpStatus.NOT_FOUND,"Employee not found with id : "+ id));
}
@ApiOperation(value = "createEmployee",notes = "Get employee base on id")
@ResponseStatus(HttpStatus.CREATED) // send HTTP 201 instead of 200 as new object created
@PostMapping
@Validated(OnCreate.class)
public Employee createEmployee(@ApiParam(value = "Employee details",required = true) @RequestBody @Valid Employee employee) {
return employeeService.createEmployee(employee);
}
@ApiOperation(value = "updateEmployee details",notes = "Update employee details")
@PutMapping()
public Employee updateEmployee(@ApiParam(value = "Employee details",required = true) @RequestBody @Validated({OnUpdate.class, Default.class}) Employee employee) {
return employeeService.updateEmployee(employee);
}
@DeleteMapping(value="/{id}")
public void deleteEmployee(@ApiParam(value = "Id of employee",required = true) @PathVariable("id") @Min(1) Integer id){
employeeService.deleteEmployee(id);
}
@PatchMapping("/{id}/dept/{deptId}")
public Employee updateEmpDepartment(@PathVariable("id") @Min(1) Integer emp_id , @PathVariable("deptId") @Min(1) Integer dept_id, @PathParam("id") @Min(1) String id) {
return employeeService.updateEmpDepartment(emp_id,dept_id);
}
@ApiOperation(value = "updateEmpDepartmentById",notes = "Update employee department based on the employee id")
@PatchMapping("/{id}")
public Employee updateEmpDepartmentById(@PathVariable("id") Integer emp_id , @RequestBody Department department) {
return employeeService.updateEmpDepartment(emp_id,department.getId());
}
@ApiOperation(value = "getEmployeesByGender",notes = "Get employee base on gender")
@GetMapping(value="/gender/{gender}")
public List<Employee> getEmployeesByGender(@PathVariable String gender) {
return employeeService.findEmployeesByGender(Gender.valueOf(gender));
}
}
Code language: Java (java)
Checking OpenAPI compliance
You can check the OpenAPI compliance of your API online .
First get hold of API specification in json format by visiting localhost:8080/v2/api-docs
page.
Then go to editor.swagger.io page and paster the json in the left side text area to see the issues with your API and correct them
You can download the source code for this blog post from GitHub
In my code I have used Spring Boot version 2.7.0, I did not face any issues but If I use Spring Boot version 2.6.x with Swagger I was getting below error when I start the application.
org.springframework.context.ApplicationContextException: Failed to start bean ‘documentationPluginsBootstrapper’
You can visit below blog post to see the different options available to resolve it.
References
https://github.com/swagger-api/swagger-core/wiki/Annotations-1.5.X