Swagger(OpenAPI Specification 3) Integration with Spring Cloud Gateway — Part 2

Pubudu Anjalee Cooray
5 min readFeb 24, 2023

--

Documentation is essential to quality and process control in any organization. Documentation encourages knowledge sharing, which empowers the team to understand how processes work, what is the status of ongoing projects and so on. When it comes to software development, it helps developers understand the code and makes it easier for them to maintain and update. Good documentation can also help to learn from mistakes. But documentation is a very time consuming task isn’t it?

When it comes to the API documentation, we can generate that easily using Swagger. It provides an UI with a collection of references and examples that help developers use the API. It explains each API with the request params, and possible responses. Also we can check APIs by sending requests with required parameters.

Photo by Gabrielle Henderson on Unsplash

Adding swagger to simple microservice, we can handle it easily. When microservices are connected with an API gateway and only the gateway is allowed to expose with a public IP address in deployment environments, we need to load the swagger UIs using the API gateway url. In this article we will discuss how to do that using Spring Cloud. I have developed the code based on a small scenario using the following technologies. Spring Cloud: Spring Eureka Server, Spring API Gateway, Spring Boot Microservices, PostgreSQL, Spring Data JPA, Swagger Open API. You can find the article related to use case using following link: https://medium.com/@pubuduc.14/develop-microservices-using-spring-cloud-part-1-2df28d782e24

The code can be found here: https://github.com/PubuduC/swagger-openapi-springcloud-integration

Let’s get started. This project is developed using Spring Boot version 3. So the configs are a bit different in version 2. We need to set up microservices and the API gateway in two ways. Let’s set up microservices first.

Setting up Microservices with OpenAPI dependency.

  <dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Spring boot starter validation will pick the version from the parent.

Enabling OpenAPI by adding following configuration class.

@OpenAPIDefinition
@Configuration
public class OpenApiConfigs {
@Bean
public OpenAPI customOpenAPI(
@Value("${openapi.service.title}") String serviceTitle,
@Value("${openapi.service.version}") String serviceVersion,
@Value("${openapi.service.url}") String url) {
return new OpenAPI()
.servers(List.of(new Server().url(url)))
.info(new Info().title(serviceTitle).version(serviceVersion));
}
}

Adding a separate config file will make sense when adding security. For an example most of the endpoints are protected and need to pass bearer token to access those endpoints. In that case we can add security schemas like below.

@Bean
public OpenAPI customOpenAPI(
@Value("${openapi.service.title}") String serviceTitle,
@Value("${openapi.service.version}") String serviceVersion,
@Value("${openapi.service.url}") String url) {
final String securitySchemeName = "bearerAuth";
return new OpenAPI()
.servers(List.of(new Server().url(url)))
.components(
new Components()
.addSecuritySchemes(
securitySchemeName,
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")))
.security(List.of(new SecurityRequirement().addList(securitySchemeName)))
.info(new Info().title(serviceTitle).version(serviceVersion));
}

Next, add configs in application.yml.

server:
servlet:
context-path: /

Make sure to make the context path like this as by default it is not set like that. Also configure the properties like below.

openapi:
service:
title: price-service doc #title that you will want to display in the swagger ui
version: 1.0.0
url: http://localhost:8080 #api gateway url
springdoc:
api-docs:
path: /price-service/v3/api-docs #these urls will used to load the swagger ui of this service by the api gateway
swagger-ui:
path: /price-service/swagger-ui.html

Add that’s all for the microservice. Configure the other microservices also like this.

Photo by Stephen O'Donnell on Unsplash

Then will move to the API Gateway setup.

Then will move to the API Gateway setup.

Add following dependencies to the pom.xml file.

  <dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
<version>2.0.2</version>
</dependency>

Due to dependency issue, I had to all following dependency under <dependencyManagement>.

<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.139</version>
</dependency>

As the api gateway loads other swagger-uis in the main app, we can enable OpenAPI by adding an annotation only.

@EnableDiscoveryClient
@SpringBootApplication
@OpenAPIDefinition(info = @Info(title = "API Gateway", version = "1.0", description = "Documentation API Gateway v1.0"))
public class ApiGatewayServiceApplication

Add following configs in application.yml.

server:
servlet:
context-path: /
port:
8080
spring:
profiles:
active:
- ${ACTIVE_SPRING_PROFILE:dev}
application:
name: api-gateway-service
springdoc:
enable-native-support: true
api-docs:
enabled: true
swagger-ui:
enabled: true
path: /swagger-ui.html
config-url: /v3/api-docs/swagger-config
urls:
- url: /v3/api-docs
name: API Gateway Service
primaryName: API Gateway Service
- url: /product-service/v3/api-docs
name: Product Service
primaryName: Product Service
- url: /price-service/v3/api-docs
name: Price Service
primaryName: Price Service

This will group your services in a drop down. Add the following routes to the API Gateway also.

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder
.routes()
.route(r -> r.path("/product-service/v3/api-docs").and().method(HttpMethod.GET).uri("lb://product-service"))
.route(r -> r.path("/price-service/v3/api-docs").and().method(HttpMethod.GET).uri("lb://price-service"))
...
.build();
}

When you want to disable swagger UI based on profiles, can use following properties in respective environment. For an example to disable logs in production environment.

spring:
config:
activate:
on-profile: prod

springdoc:
swagger-ui:
enabled: false
api-docs:
enabled: false

Also can use the the profile annotation in OpenApiConfigs.

@OpenAPIDefinition
@Configuration
@Profile({"local", "dev"})
public class OpenApiConfigs

We can use swagger annotations to describe the endpoints to make clear.

@Operation(summary = "Calculate price by product and units needed", description = "Returns price for given product with amount")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved", content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = {
@ExampleObject(
name = "200 Response",
summary = "Price calculated successfully",
value = "3389.08")})),
@ApiResponse(responseCode = "404", description = "Not found - The product was not found", content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = {
@ExampleObject(
name = "404 Response",
summary = "404 from the service directly",
value =
"{\"timestamp\": \"2023-02-18T01:20:33.0725725\","
+ "\"message\": \"Product not found by id : 1\""
+ "}")}))})
@GetMapping
public float priceGenerator(@RequestParam long id, @RequestParam int unitsRequired){
return priceService.calculatePrice(id, unitsRequired);
}

That’s it.

Then we can load the swagger UIs like below.

API Gateway Swagger UI
Product Service Swagger UI
Price Service Swagger UI

--

--

Pubudu Anjalee Cooray

Senior Software Engineer with 4 years of experience. Graduated from Department of Computer Science and Engineering at University of Moratuwa, Sri lanka.