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

Swagger

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

https://swagger.io/

Similar Posts