Thanks to visit codestin.com
Credit goes to dzone.com

DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Spring Microservices RESTFul API Documentation With Swagger Part 1
  • Build a REST API With Just 2 Classes in Java and Quarkus
  • Micronaut vs Spring Boot: A Detailed Comparison
  • Best Practices for Designing Resilient APIs for Scalability and Reliability

Trending

  • Building Scalable and Resilient Data Pipelines With Apache Airflow
  • Breaking Bottlenecks: Applying the Theory of Constraints to Software Development
  • Google Cloud Document AI Basics
  • Unlocking AI Coding Assistants Part 3: Generating Diagrams, Open API Specs, And Test Data
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Rest API Documentation and Client Generation With OpenAPI

Rest API Documentation and Client Generation With OpenAPI

In this post, see how to use OpenAPI to generate REST API documentation and Java clients for a Spring Boot project.

By 
Prasanth Gullapalli user avatar
Prasanth Gullapalli
·
Jun. 22, 21 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
14.1K Views

Join the DZone community and get the full member experience.

Join For Free

Microservices architecture helps in building an application as a suite of multiple fine-grained smaller services. Each of these services run in its own process and are independently deployable. They may have been developed in different programming languages and may use different data storage techniques and communicate with each other via lightweight mechanisms like RESTful APIs, message queues etc. Now that services(API clients) need to know how to interact with each other, there is a need of API documentation that should be clear, informative, and easy to go through.

OpenAPI makes documenting these RESTful services very easy and can be done by adding few annotations to a Spring-based project. Besides rendering documentation, it allows the API consumers to interact with the APIs without having their own implementation logic in place. Also, it can be used to generate the API client libraries for over 50+ languages. In this post, I’ll cover how to use OpenAPI to generate REST API documentation and Java clients for a Spring Boot project.

Our sample application implements a set of REST endpoints to expose the employees and departments resources of an organization. Following is the UML model of the sample application we will look at:

The following controller exposes the Rest APIs:

Java
 
package com.xyz.openapi.server.controller;
 
import com.xyz.openapi.server.model.Department;
import com.xyz.openapi.server.model.Employee;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
 
@RestController
@RequestMapping("/api")
public class OrganizationController {
    private List<Employee> employees = new ArrayList<>();
 
    @GetMapping(value = "/employees")
    public EmployeeList getAllEmployees(@RequestParam(required = false) String deptId) {
        List<Employee> employees = this.employees.stream()
                .filter(emp -> deptId == null ||
                        (deptId != null && emp.getDepartment() != null && emp.getDepartment().getId().equals(deptId)))
                .collect(Collectors.toList());
        return EmployeeList.of(employees);
    }
 
    @GetMapping(value = "/employees/{id}")
    public Employee getEmployee(@PathVariable String id) {
        Optional<Employee> optional = employees.stream()
                .filter(emp -> emp.getId().equals(id))
                .findAny();
        if(optional.isEmpty()) {
            throw new IllegalArgumentException("Employee does not exist for id: "+id);
        }
        return optional.get();
    }
 
    @PostMapping(value = "/employees")
    public String createEmployee(@RequestBody Employee emp){
        emp.setId(UUID.randomUUID().toString());
        employees.add(emp);
        return emp.getId();
    }
 
    @PutMapping(value = "/employees")
    public String updateEmployee(Employee updatedEmp){
        employees.stream()
                .filter(e -> updatedEmp.getId().equals(e.getId()))
                .findFirst()
                .map(emp -> {
                    BeanUtils.copyProperties(updatedEmp, emp);
                    return emp;
                })
                .orElseThrow();
        return updatedEmp.getId();
    }
 
    // Few other APIs for Department resource follows here
}

To add OpenAPI to our project, the following dependency has to be added to gradle:

Groovy
 
// OpenAPI Documentation
dependency("org.springdoc:springdoc-openapi-ui:1.5.8")

Now add the OpenAPI configuration to the project:

Java
 
@Configuration
public class SwaggerConfiguration {
    @Bean
    public OpenAPI openAPI() {
        Contact contact = new Contact();
        contact.setEmail("[email protected]");
        contact.setName("XYZ Support");
        contact.setUrl("http://www.xyz.com");
        return new OpenAPI()
                .info(new Info().title("Employee APIs").description("Description here..")
                        .version("1.0").contact(contact)
                        .license(new License().name("2015-2021 XYZ LLC All Rights Reserved")));
    }
}

Now when we run the application, the API documentation can be accessed at http://localhost:8080/swagger-ui/. Here is a snapshot of how it looks like: 

The schemas section of the documentation lists all the model classes(and their structure) involved in the API transactions. Following is the schema for the sample application: 

We can annotate the APIs with Schema definitions to have more control over the API documentation generation process. In the below code snippet, we have made use of the following annotations:

  1. Operation: Define additional properties for the API Operation.
  2. ApiResponses: Container for repeatable ApiResponse annotation
  3. ApiResponse: The annotation may be used at method level or as field of Operation to define one or more responses of the Operation.
  4. Schema: The annotation may be used to define a Schema for a set of elements of the OpenAPI spec, and/or to define additional properties for the schema. It is applicable e.g. to parameters, schema classes (aka “models”), properties of such models, request and response content, header.
Java
 
@RestController
@RequestMapping("/api")
@Tag(name = "Organization Controls", description = "API Endpoints to operate on Employee and Departments")
public class OrganizationController {
    private List<Employee> employees = new ArrayList<>();
    private List<Department> departments = new ArrayList<>();
 
    @GetMapping(value = "/employees")
    @Operation(summary = "If departmentId is not passed, get all employees. Otherwise get employees from specific department.")
    @ApiResponses({
            @ApiResponse(responseCode = "200", content = {
                    @Content(schema = @Schema(implementation = EmployeeList.class))
            }),
            @ApiResponse(responseCode = "500", content = {
                    @Content(schema = @Schema(implementation = ErrorResponse.class))
            })
    })
    public EmployeeList getAllEmployees(@RequestParam(required = false) String deptId) {
        List<Employee> employees = this.employees.stream()
                .filter(emp -> deptId == null ||
                        (deptId != null && emp.getDepartment() != null && emp.getDepartment().getId().equals(deptId)))
                .collect(Collectors.toList());
        return EmployeeList.of(employees);
    }
 
    // More methods here
}

After adding the above annotations, here is how the API documentations looks like:

One more benefit of OpenAPI documentation is that the APIs can be executed on UI directly:

Now that the documentation is ready, the next step is to generate Java API clients. We will use OpenAPI generator gradle plugin for this purpose. The other services can simply use the API client once generated, instead of writing their own client, to interact with API. The generated client encapsulates the logic of making REST calls to the exposed APIs behind the scenes. In order to do this, we need to add the following plugin to gradle: 

Java
 
// https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator-gradle-plugin/README.adoc
plugins {
    id "org.openapi.generator" version "5.1.0"
}
 
openApiGenerate {
    // The input specification to validate. Can be accessed at http://localhost:8080/v3/api-docs
    inputSpec.set("$rootDir/api-docs.json")
    // The output target directory into which code will be generated
    outputDir.set("$rootDir/thinclient")
 
    groupId.set("com.xyz.openapi")
    id.set("openapi-thinclient")
    version.set("0.0.1-SNAPSHOT")
 
    // Package for generated API classes
    apiPackage.set("com.xyz.openapi.thinclient.api")
    // Root package for generated code
    invokerPackage.set("com.xyz.openapi.thinclient.invoker")
    // All the model classes being used in API will be in this package
    modelPackage.set("com.xyz.openapi.thinclient.model")
 
    // Language in which client has to be generated. Please refer: https://openapi-generator.tech/docs/generators/java/
    generatorName.set("java");
    // REST API library to use. For Ex: jersey1, jersey2, okhttp-gson etc
    library.set("resttemplate")
    // A map of options specific to a generator. To see the full list of generator-specified parameters, please refer to [generators docs](https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators.md).
    configOptions.put("serializationLibrary", "jackson")
}

Now we can run gradle openApiGenerate to generate the classes in the path configured above in gradle configuration. Here is the structure of generated API client generated: 

We can now publish the generated client to repository so that it can be used like any other dependency in the other micro-services. Also, gradle openApiGenerators lists all the languages in which the client can be generated. So, we can generate the client in the language of our choice from the specified list.

You can find the source code for this post on GitHub.

API Documentation REST

Published at DZone with permission of Prasanth Gullapalli. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Spring Microservices RESTFul API Documentation With Swagger Part 1
  • Build a REST API With Just 2 Classes in Java and Quarkus
  • Micronaut vs Spring Boot: A Detailed Comparison
  • Best Practices for Designing Resilient APIs for Scalability and Reliability

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: