Overview:

Swagger is a tool-set for RESTful API development and based on the OpenAPI Specification (formerly called the Swagger Specification) that includes automated documentation, code generation, and test case generation.

The goal of the specification is to create the RESTful documented contract for your API, detailing all of its resources and operations in a human and machine readable format for easy development, discovery, and integration.

SpringFox is a Java and SpringBoot library project of that encapsulates the Swagger automated documentation generation tool-set. SpringFox works by examining an application and associated REST controller end-points at start-up to infer API semantics based on spring configurations, class structure and various compile time java Annotations.

This article will show you how to configure and provision you SpringBoot project, so that you can automatically generate web-based REST API documentation as shown below for all of your exposed end points. Code presented in this article is in BitBucket.

Prerequisites:

Procedure:

We are going to re-visit the SpringBoot Spring-JPA-Hibernate “Tending your Data Relationships” Project and retro-actively apply the SpringFox Swagger configuration to fully document this REST API. Code presented in this article is in BitBucket.

Setting Up the SpringFox Dependencies in the POM:

The POM changes are as follows, and can be used to add SpringFox Swagger to any of your SpringBoot REST applications.

  • As of the date of this article, version 2.7.0 was the latest SpringFox release.
  • Ensure that all three of the io.springfox artifacts are in the same release version.
...

        <!-- SpringFox Swagger -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>2.7.0</version>
            <scope>compile</scope>
        </dependency>
        
...

Setting Up the SpringFox API Information in application.properties:

As shown, add the following SpringFox Swagger API settings section to your application.properties file, and set appropriately for your application. This is used to create the springfox.documentation.service.ApiInfo object that is part of the over-all Swagger SpringFox “docket” bean(s) representing each API controller end-point grouping. This feeds the Swagger web interface author and API contact information as shown above.

...

#------------------SwaggerConfig implementation properties------------------  

#The title for the spring boot service to be displayed on swagger UI. It will refer to the above value.  
swagger.title=API End Points
 
#The description of the spring boot service. It will refer to the above value.  
swagger.description=Provides API for Rest Service
 
#The version of the service. It will refer to the version specified in pom.xml.  
swagger.version=1
 
#The terms of service url for the service if applicable.  
swagger.termsOfServiceUrl=https://www.paulsdevblog.com/disclaimer-notice/
 
##The contact name for the service.  
swagger.contact.name=PaulsDevBlog.com

#The contact url for the service  
swagger.contact.url=https://www.paulsdevblog.com
 
#The contact email for the service  
swagger.contact.email=todo_dev_email@todo.com
 
#The license information about this service  
swagger.license=AS-IS Example PaulsDevBlog.com, see disclaimer
swagger.licenseUrl=https://www.paulsdevblog.com/disclaimer-notice/

....

Create the SwaggerConfig class

Create a SwaggerConfig class as shown.

  1. I typically create a config package for this and similar application configuration classes.
  2. The class will be annotated as a @Component and @Configuration to ensure it resides as a bean and instantiated at the very beginning of application startup.
  3. The @EnableSwagger2 will auto-wire in the SpringFox and Swagger dependencies and start up the auto-discovery functions to document the the REST application’s controller methods.
  4. Next, we will use the @Value annotation to auto-assign each of the private SpringFox-Swagger API-info variables to the applicable setting within the application.properties file (above).
  5. The apiEndPointsInfo method will generate the springfox.documentation.service.ApiInfo object using the private SpringFox-Swagger API-info variables.
  6. In this example, I wanted the Swagger GUI to separate the REST end points by the respective controller class. Therefore, I created a separate Swagger documentation configuration bean, known as a “Docket” bean, for each API group.
    • So, I set the “.apis” section of the Docket builder to the base controller package
    • And refined the included REST end points through the
      “.paths( PathSelectors.ant( “/path/**”) )
      NOTE: The double asterisk takes care of the API query parameters.
package com.paulsdevblog.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * Configuration class for Swagger SpringFox
 *
 * @author PaulsDevBlog.com
 */
@Component
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    
    private static final Logger logger = LoggerFactory.getLogger( SwaggerConfig.class );
    
    /** The title for the spring boot service to be displayed on swagger UI.  */  
    @Value("${swagger.title:spring.application.name:app_title}")  
    private String title;  
    
    /** The description of the spring boot service. */  
    @Value("${swagger.description:spring.application.description:app_description}")  
    private String description;  
   
    /** The version of the service. */  
    @Value("${swagger.version:spring.application.version:versionxxx}")  
    private String version;  
   
    /** The terms of service url for the service if applicable. */  
    @Value("${swagger.termsOfServiceUrl:terms_of_service_url:}")  
    private String termsOfServiceUrl;  
   
    /** The contact name for the service. */  
    @Value("${swagger.contact.name:contact_name}")  
    private String contactName;  
   
    /** The contact url for the service. */  
    @Value("${swagger.contact.url:contact_url}")  
    private String contactURL;  
   
    /** The contact email for the service. */  
    @Value("${swagger.contact.email:email_address}")  
    private String contactEmail;  
   
    /** The license for the service if applicable. */  
    @Value("${swagger.license:license_body}")  
    private String license;  
   
    /** The license url for the service if applicable. */  
    @Value("${swagger.licenseUrl:client_licenseUrl}")  
    private String licenseURL;  

    
    /**  
    * This method will return the API info object to swagger which will in turn 
    * display the information on the swagger UI.  
    * 
    * See the active application.properties file for adjusting attributes.
    *  
    * @return the API information object
    */ 
    private ApiInfo apiEndPointsInfo() {
        
        return new ApiInfoBuilder()
                    .title( this.title )
                    .description( this.description )
                    .contact( new Contact(this.contactName, this.contactURL, this.contactEmail ))
                    .license( this.license )
                    .licenseUrl( this.licenseURL )
                    .version( this.version )
                    .build();

    }
    
    /**  
     *  Swagger API Endpoint for Customer
     * 
     * This method will return the Docket API object to swagger for the 
     * this micro-service application defined end-points, which will in turn 
     * display the information on the swagger UI.  
     *  
     * Refer the URL http://{ip-address or host-name}:{service.port}/{server.contextPath}/swagger-ui.html  
     * 
     * @return the docket object
     */ 
    @Bean
    public Docket docketAPICustomer() {
        
        Docket docket = new Docket( DocumentationType.SWAGGER_2 )
            .groupName( "APICustomer" )
            .select()
            .apis( RequestHandlerSelectors.basePackage("com.paulsdevblog.springboothibernaterelationships.controller") )
            .paths(PathSelectors.ant( "/customer/**"))
            .build()
            .apiInfo( apiEndPointsInfo() );
        
        String logging_message = "Configured SWAGGER Group: [" + String.valueOf( docket.getGroupName() ) + "]" ;
        logger.info( logging_message );
        
        return docket;
    }
    
    /**  
     *  Swagger API Endpoint for CustomerLocation
     * 
     * This method will return the Docket API object to swagger for the 
     * this micro-service application defined end-points, which will in turn 
     * display the information on the swagger UI.  
     *  
     * Refer the URL http://{ip-address or host-name}:{service.port}/{server.contextPath}/swagger-ui.html  
     * 
     * @return the docket object
     */ 
    @Bean
    public Docket docketAPICustomerLocation() {
        
        Docket docket = new Docket( DocumentationType.SWAGGER_2 )
            .groupName( "APICustomerLocation" )
            .select()
            .apis( RequestHandlerSelectors.basePackage("com.paulsdevblog.springboothibernaterelationships.controller") )
            .paths(PathSelectors.ant( "/customerlocation/**"))
            .build()
            .apiInfo( apiEndPointsInfo() );
        
        String logging_message = "Configured SWAGGER Group: [" + String.valueOf( docket.getGroupName() ) + "]" ;
        logger.info( logging_message );
        
        return docket;
    }
    
    /**  
     *  Swagger API Endpoint for LocationRegion
     * 
     * This method will return the Docket API object to swagger for the 
     * this micro-service application defined end-points, which will in turn 
     * display the information on the swagger UI.  
     *  
     * Refer the URL http://{ip-address or host-name}:{service.port}/{server.contextPath}/swagger-ui.html  
     * 
     * @return the docket object
     */ 
    @Bean
    public Docket docketAPILocationRegion() {
        
        Docket docket = new Docket( DocumentationType.SWAGGER_2 )
            .groupName( "APILocationRegion" )
            .select()
            .apis( RequestHandlerSelectors.basePackage("com.paulsdevblog.springboothibernaterelationships.controller") )
            .paths(PathSelectors.ant( "/locationregion/**"))
            .build()
            .apiInfo( apiEndPointsInfo() );
        
        String logging_message = "Configured SWAGGER Group: [" + String.valueOf( docket.getGroupName() ) + "]" ;
        logger.info( logging_message );
        
        return docket;
    }
    
    
    /**  
     *  Swagger API Endpoint for LocationType
     * 
     * This method will return the Docket API object to swagger for the 
     * this micro-service application defined end-points, which will in turn 
     * display the information on the swagger UI.  
     *  
     * Refer the URL http://{ip-address or host-name}:{service.port}/{server.contextPath}/swagger-ui.html  
     * 
     * @return the docket object
     */ 
    @Bean
    public Docket docketAPILocationType() {
        
        Docket docket = new Docket( DocumentationType.SWAGGER_2 )
            .groupName( "APILocationType" )
            .select()
            .apis( RequestHandlerSelectors.basePackage("com.paulsdevblog.springboothibernaterelationships.controller") )
            .paths(PathSelectors.ant( "/locationtype/**"))
            .build()
            .apiInfo( apiEndPointsInfo() );
        
        String logging_message = "Configured SWAGGER Group: [" + String.valueOf( docket.getGroupName() ) + "]" ;
        logger.info( logging_message );
        
        return docket;
    }
}

Add ComponentScan to your Main Class

Usually, SpringBoot will find all of the classes marked for auto-wiring and component/bean instantiation, but I have found that it sometimes needs a little help via the @ComponentScan annotation.

  1. In your Main class, add the @ComponentScan annotation with respective the array listing all packages in your application hierarchy that contain either @Component, @Service, @RestController or similar bean annotations.
package com.paulsdevblog;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Main entry point for application
 * 
 * @author PaulsDevBlog.com
 */
@SpringBootApplication
@ComponentScan({    
                "com.paulsdevblog",
                "com.paulsdevblog.config",
                "com.paulsdevblog.springboothibernaterelationships.domain",
                "com.paulsdevblog.springboothibernaterelationships.domain.model",
                "com.paulsdevblog.springboothibernaterelationships.domain.repository",
                "com.paulsdevblog.springboothibernaterelationships.domain.service",
                "com.paulsdevblog.springboothibernaterelationships.domain.views",
                "com.paulsdevblog.utility"
})
public class SpringBootSpringFoxExampleApplication {
    
    public static final Logger logger = LoggerFactory.getLogger(SpringBootSpringFoxExampleApplication.class );

    public static void main(String[] args) {
        
        SpringApplication.run(SpringBootSpringFoxExampleApplication.class, args);
        
        logger.info("--Application Started--");
        logger.info("SpringBoot and Hibernate Entity Relationships Micro-Service!");
    }
}

Swagger API Documentation Annotations

The last piece to configuring Swagger SpringFox is to annotate the REST endpoints appropriately.

  1. As shown, the @ApiOperation annotation provides a brief description of the end point method.
  2. The @ApiResponses annotation lists possible return status codes, and applicable response POJO class structure returned.
    NOTE: SpringFox does not understand JSON Views annotations, and will always show the entire object being returned.
  3. The @ApiParam annotation that further describes the method parameters.
...

import io.swagger.annotations.*;

...

    /**
     * REST GET Find list of Customer matching customerName
     * 
     * @param customerName parameter code
     * @return list of Customer 
     * @throws ServiceLayerDataException 
     */
    @ApiOperation(value = "Find Customer entities for CustomerName  ")
    @ApiResponses(value = {
                        @ApiResponse( code = 200, message = "Successful", response = Customer.class  ),
                        @ApiResponse( code = 400, message = "Bad request" ),
                        @ApiResponse( code = 401, message = "Unauthorized" ),
                        @ApiResponse( code = 403, message = "Forbidden" ),
                        @ApiResponse( code = 404, message = "Not Found" ),
                        @ApiResponse( code = 406, message = "Unacceptable or invalid fields" )
                        }
    )
    @JsonView(EntityViews.Details.class)
    @RequestMapping(value={"/name/{customername}" }, 
                    method=RequestMethod.GET,
                    produces = { MediaType.APPLICATION_JSON_VALUE }
                    )
    public ResponseEntity<List<Customer>> findByCustomerCode( 
                @ApiParam (value = "customerName", required = true) 
                @PathVariable( value = "customername", required = true ) String customerName 
    ) throws ServiceLayerDataException {

        //Avoid nullpointer and ensure we have valid string value
        customerName = String.valueOf( customerName );
        
        //Get list from service
        List<Customer> listEntities = this.customerService.findByCustomerName(customerName);
        
        if ( listEntities == null ) {
            return ResponseEntity.status( HttpStatus.NOT_FOUND ).build(); //return 404, with null body
        } else {
            return new ResponseEntity<List<Customer>>( listEntities, HttpStatus.OK );
        }
    }


...

Example:

BitBucket Repo and Project Code

The return from just a few lines of configuration is impressive. Nothing makes your API more “real” than good GET/POST/PUT end-point documentation. I hoped you enjoyed this article; please like and link: paulsDevBlog.End();

References: