This article shows how to use Open API specification and Swagger tools to build a REST service with spring boot and Maven. It describes how the Open API may be used as a contract and how to use Maven to generate Controller interfaces at build time with the API First Approach. It also shows how to configure Swagger UI to use a static Open API JSON file and the reasons why you may want to do it. The complete code can be found right here on allAroundJava Github.

Swagger’s Open Api

Swagger’s Open API is a way to describe the functionality of REST services including the resources they expose, data they exchange, responses they return and many more. Open API file can serve both as a documentation or be helpful when designing the API.
It is a very convenient way of exchanging API definition information between parties building and consuming the API and doing so brings many benefits. For instance, thanks to Swagger tools you can quickly build REST Service mocks based on prepared API specification file to speed up development process.
Because this format is readable to machines, we can also use it to generate our Rest Controller classes, DTO objects and more.
With the help of additional libraries, we can also use the Open Api definition file as helpful API documentation allowing to call our rest service methods and examine how they work. We will do that further in the article.

The Open API definition file comes in two formats – JSON or YAML. Here’s an example of Open API YAML definition file for a REST service allowing to list and book doctor visits at a medical center.

swagger: "2.0"
info:
  description: "This is a specification for DoctorBooking Api. 
  You can find out more about this application at 
  [htp://allaroundjava.com](http://allaroundjava.com)"
  version: "1.0"
  title: "Doctor Booking Application"
  contact:
    email: "adam@allaroundjava.com"
host: "localhost:8080"
basePath: "/"
tags:
  - name: "doctors"
    description: "Everything about Doctors in the system"
schemes:
  - "http"
paths:
  /doctors:
    post:
      tags:
        - "doctors"
      summary: "Add a new doctor to Medical Clinic"
      description: ""
      operationId: "createDoctor"
      consumes:
        - "application/xml"
      produces:
        - "application/xml"
      parameters:
        - in: "body"
          name: "DoctorDto"
          description: "Doctor object that needs to be added to the clinic"
          required: true
          schema:
            $ref: "#/definitions/DoctorDto"
      responses:
        201:
          description: "Created Doctor"
          schema:
            $ref: "#/definitions/DoctorDto"
        405:
          description: "Invalid input"

This part of the file specifies only a single resource and a single associated HTTP method. For a full reference go to allAroundJava’s Github.

Approaches to building REST services with Open API

The traditional approach to REST service development is to build your Controllers first and then use Swagger annotations to describe controllers and their methods. The fact that Open API specification is readable for human as well as machines, opens up some new possibilities.
The API first (or design first) approach bases on designing the API specification first and following it with code to match the specification in the next step. Prepared document is an input to both building and testing the API.
With some help from build tools like Maven, we can generate the controller interfaces, DTOs or tests at build time.
Preparing a design document first, usually leads to a better overview of the structure, hence a better API design and can free up some resources as it allows for mocking even before any service is physically available. In nature it’s similar to SOAP’s WSDL, but it’s much easier to build.

Building REST API with Open API Specification and Design First Approach

Utilizing this approach requires getting familiar with the Open API semantics. The format is really simple and does not require any previous experience. It’s best to observe the Pet Store example on Swagger’s pages to understand how things work. Both IntelliJ and Swagger editor come as a very handy tools to prepare the document. You can view the full document which we’ll use in this article right here.
Once the API definition file is ready, we can use it as an input to generate our Rest Controller interfaces.

Using swagger YAML to generate interfaces with Maven

Now generating the interfaces from an API specification requires some effort as the swagger-codegen-maven-plugin lacks good documentation. Here is how the plugin configuration may look like.

<plugin>
	<groupId>io.swagger</groupId>
	<artifactId>swagger-codegen-maven-plugin</artifactId>
	<version>2.4.8</version>
	<executions>
		<execution>
			<goals>
				<goal>generate</goal>
			</goals>
			<configuration>
				<inputSpec>${project.basedir}/src/main/resources/swagger.yaml</inputSpec>
				<language>spring</language>
				<library>spring-boot</library>
				<apiPackage>com.allaroundjava.controller</apiPackage>
				<modelPackage>com.allaroundjava.dto</modelPackage>
				<generateApis>true</generateApis>
				<generateApiTests>false</generateApiTests>
				<generateModelTests>false</generateModelTests>
				<generateApiDocumentation>false</generateApiDocumentation>
				<generateModels>false</generateModels>
				<generateSupportingFiles>false</generateSupportingFiles>
				<languageSpecificPrimitives>true</languageSpecificPrimitives>
				<typeMappings>
					<typeMapping>OffsetDateTime=java.time.LocalDateTime</typeMapping>
				</typeMappings>
				<importMappings>
					<importMapping>LocalDateTime=OffsetDateTime</importMapping>
				</importMappings>
				<configOptions>
					<interfaceOnly>true</interfaceOnly>
					<java8>false</java8>
					<dateLibrary>java8</dateLibrary>
				</configOptions>
			</configuration>
		</execution>
	</executions>
</plugin>
<plugin>
	<groupId>org.codehaus.mojo</groupId>
	<artifactId>build-helper-maven-plugin</artifactId>
	<version>3.0.0</version>
	<executions>
		<execution>
			<phase>generate-sources</phase>
			<goals>
				<goal>add-source</goal>
			</goals>
			<configuration>
				<sources>
					<source>${project.build.directory}/generated-sources/swagger/src/main/java</source>
				</sources>
			</configuration>
		</execution>
	</executions>
</plugin>

The first plugin generates interface classes from an input YAML file. It uses settings for language and library to generate spring boot ready interfaces. The files will be generated in Maven’s target directory. This is the reason why we need the build-helper plugin. It will use generated files as input for compilation of classes under project source directory.
The generated interface will look as follows. (This is only a part of the interface class)

@javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "2019-09-28T16:54:02.452+02:00")

@Api(value = "doctors", description = "the doctors API")
public interface DoctorsApi {

    Logger log = LoggerFactory.getLogger(DoctorsApi.class);

    default Optional getObjectMapper() {
        return Optional.empty();
    }

    default Optional getRequest() {
        return Optional.empty();
    }

    default Optional getAcceptHeader() {
        return getRequest().map(r -> r.getHeader("Accept"));
    }

    @ApiOperation(value = "Add a new doctor to Medical Clinic", nickname = "createDoctor", notes = "", response = DoctorDto.class, tags={ "doctors", })
    @ApiResponses(value = { 
        @ApiResponse(code = 201, message = "Created Doctor", response = DoctorDto.class),
        @ApiResponse(code = 405, message = "Invalid input") })
    @RequestMapping(value = "/doctors",
        produces = { "application/xml" }, 
        consumes = { "application/xml" },
        method = RequestMethod.POST)
    default ResponseEntity createDoctor(@ApiParam(value = "Doctor object that needs to be added to the clinic" ,required=true )  @Valid @RequestBody DoctorDto doctorDto) {
        if(getObjectMapper().isPresent() && getAcceptHeader().isPresent()) {
            if (getAcceptHeader().get().contains("application/xml")) {
                try {
                    return new ResponseEntity<>(getObjectMapper().get().readValue("  123456789  aeiou", DoctorDto.class), HttpStatus.NOT_IMPLEMENTED);
                } catch (IOException e) {
                    log.error("Couldn't serialize response for content type application/xml", e);
                    return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
                }
            }
        } else {
            log.warn("ObjectMapper or HttpServletRequest not configured in default DoctorsApi interface so no example is generated");
        }
        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
    }

Now you may ask why is there a default implementation when the plugin has a setting

<interfaceOnly>true</interfaceOnly>

And this is a very good question. The swagger plugin generates default implementations whenever you decide to use Java 8 classes to generate code. Yes another very good question is – hey but

<java8>false</java8>

And that’s right. Apparently when you want to use Java 8’s time api classes, java 8 is implicitly used and hence default implementations get created. I did not succeed to find it in the documentation or work around it.

Now creating a controller class is just a matter of implementing the API interface.

@RestController
public class DoctorController implements DoctorsApi {
    private final DoctorService doctorService;

    @Autowired
    public DoctorController(DoctorService doctorService) {
        this.doctorService = doctorService;
    }

    public ResponseEntity createDoctor(@RequestBody DoctorDto doctorInput) {
        Doctor doctor = DoctorDtoMapper.toEntity(doctorInput);
        doctorService.addDoctor(doctor);
        DoctorDto doctorDto = DoctorDtoMapper.toDto(doctor);
        doctorDto.add(linkTo(methodOn(DoctorController.class).getDoctor(doctor.getId())).withSelfRel());
        return ResponseEntity.status(HttpStatus.CREATED).body(doctorDto);
    }

Testing API with Swagger – UI

Swagger provides us with a great tool called Swagger UI. It presents the API in an easily readable, visual form and allows to test all our controller methods by calling the resource endpoints according to specification. This is by no means an automated test. It requires manual triggering, but it’s a very helpful tool, especially when learning how the API works. Here’s how it looks like

##Image

The Swagger UI is easily enabled with a Springfox Swagger UI library.

By default, Swagger UI page, will utilize Swagger annotations on controller or interface classes to generate a JSON file, used as a source of data for the page. With API First approach this seems a little odd. We already have an API specification which we can use as a source of information about our API. We’ve successfully built our YAML file before. Additionally, the annotations, auto generated with swagger-codegen tool, do not contain all the information found in our API specification file. But there’s a way to fix it.

Serving static JSON for Swagger-UI page

There is a way to feed our API specification as a source of data to Swagger UI page. The page can then be used to test our API against the specification.
Because Swagger UI page uses JSON as a data input, the first step is to convert our YAML file to JSON. That can be easily done in Swagger Editor. The next thing is to add the Swagger UI dependency.

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

Now here’s a tricky bit. I wasn’t able to add this dependency without configuring my project pom with spring-boot-starter-parent. Attempting to pick on spring boot libraries in versions I desired ended up in a dependency conflict around spring-plugin library.

The next point is to create a Controller which will enable all the standard Swagger UI endpoints. By default, this is configured automatically by enabling @EnableSwagger2 annotation, but we’re trying to avoid auto configuration and we’re skipping the annotation here.

@RestController
public class SwaggerController {

    @RequestMapping(method = RequestMethod.GET, 
	path = "/v2/api-docs", produces = MediaType.APPLICATION_JSON_VALUE)
    public Resource apiJsonEndpoint() {
        return new ClassPathResource("swagger.json");
    }

    @RequestMapping(method = RequestMethod.GET, path = "/swagger-resources", 
	produces = MediaType.APPLICATION_JSON_VALUE)
    public Object swaggerResources() {
        return ImmutableList.of(ImmutableMap.of(
                "name", "default",
                "url", "/v2/api-docs",
                "location", "/v2/api-docs", // this should match the endpoint exposing Swagger JSON
                "swaggerVersion", "2.0"));
    }

    @RequestMapping(method = RequestMethod.GET, 
	path = "/swagger-resources/configuration/security", 
	produces = MediaType.APPLICATION_JSON_VALUE)
    public Object swagerSecurityConfig() {
        return ImmutableList.of(ImmutableMap.of(
                "apiKeyVehicle", "header",
                "scopeSeparator", ",",
                "apiKeyName", "api_key"));
    }

    @RequestMapping(method = RequestMethod.GET, 
	path = "/swagger-resources/configuration/ui", 
	produces = MediaType.APPLICATION_JSON_VALUE)
    public Object swaggerUiConfig() {
        return "{\"deepLinking\":true," +
                "\"displayOperationId\":false," +
                "\"defaultModelsExpandDepth\":1," +
                "\"defaultModelExpandDepth\":1," +
                "\"defaultModelRendering\":\"example\"," +
                "\"displayRequestDuration\":false," +
                "\"docExpansion\":\"none\"," +
                "\"filter\":false," +
                "\"operationsSorter\":\"alpha\"," +
                "\"showExtensions\":false," +
                "\"tagsSorter\":\"alpha\"," +
                "\"validatorUrl\":\"\"," +
                "\"apisSorter\":\"alpha\"," +
                "\"jsonEditor\":false," +
                "\"showRequestHeaders\":false," +
                "\"supportedSubmitMethods\":[\"get\",\"put\",\"post\",\"delete\",\"options\",\"head\",\"patch\",\"trace\"]}";
    }
}

The first endpoint is the one where we expose a static resource – our swagger.json file, which describes the REST API. The remaining three endpoints are standard Swagger UI endpoints.
The final part is to configure view controllers and resource handlers.

@Configuration
@EnableWebMvc
public class SwaggerConfig implements WebMvcConfigurer {
    @Value("${app.swagger-ui.redirectPrefix}")
    private String redirectPrefix;

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addRedirectViewController(redirectPrefix + "/v2/api-docs", "/v2/api-docs");
        registry.addRedirectViewController(
                redirectPrefix + "/swagger-resources/configuration/ui", "/swagger-resources/configuration/ui");
        registry.addRedirectViewController(
                redirectPrefix + "/swagger-resources/configuration/security", "/swagger-resources/configuration/security");
        registry.addRedirectViewController(redirectPrefix + "/swagger-resources", "/swagger-resources");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/" + redirectPrefix + "/swagger-ui.html**")
                .addResourceLocations("classpath:/META-INF/resources/swagger-ui.html");
        registry.addResourceHandler("/" + redirectPrefix + "/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

And the Swagger UI page with JSON being our API specification is served right away under [service root]/[redirect-prefix]/swagger-ui.html.
If you need more help serving a static JSON as a data feed for Swagger UI page, please use this DZONE tutorial. Above instructions were based on it.

Here’s some more interesting articles around REST:

Leave a Comment