Api-Gateway

Spring cloud gateway

Nikitha Gullapalli
4 min readMay 30, 2023

Api-Gateway is the first layer that is hit when an REST call is made. All common functions common to all services can be done here like authentication, monitoring, logging,etc. Then we can add routes to respective REST call.

Api-Gateway gets the instance of the service to call from the naming server/load balancer (Eureka) and calls the respective service. Lets look at how to implement an api-gateway and route the request call to the service.

Spring cloud gateway

  • Efficient way to route to APIs
  • Provide cost cutting concerns: Security, monitoring/Metrics are some examples on what can be implemented in gateway.
  • Functions common to all services are implemented here.

Features:

  • Match routes on any request attribute
  • Define Predicates and Filters
  • Integrates with spring cloud discovery client (Load Balancing)
  • Path Rewriting

Pre-requisites:

Setup micro services and Eureka load balancer. Here are steps to set up load balancer (Eureka)

Steps to create an API-Gateway

  1. Create a spring boot project spring initializr with dependency shown below.

2. Add port, name and eureka url in application.properties

spring.application.name= api-gateway

#http://localhost:8765
server.port=8765

#------------Eureka naming server------------
eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka

#------------api-gateway server------------
spring.cloud.gateway.discovery.locator.enabled=true
#spring.cloud.gateway.discovery.locator.lower-case-service-id=true

Now start your load balancer (Eureka server) and your api-gateway (right click on GatewayApplication.java → run as java application)

Start your micro-services aswell. In my case its currency-conversion and currency-exchange service.

When you open eureka server url in browser, you should see api-gateway and your services running.

3. Try making a rest call via api-gateway. Call is fail.

Url: http://localhost:8765/currency-exchange/from/USD/to/INR

Reason is we need to find the service name in load balancer (eureka server) and route it via the load balancer. So if I want to make a call to currency-exchage. I should see if the service is up on Eureka, copy its name and put it in the url as shown below.

http://localhost:8765/CURRENCY-EXCHANGE-SERVICE/currency-exchange/from/USD/to/INR

4. Now if you notice just the service name is in caps. I want it smaller case, so that my url will look uniform. I can achieve this by adding the below line in application.properties file.

spring.cloud.gateway.discovery.locator.lower-case-service-id=true

Now try http://localhost:8765/currency-exchange-service/currency-exchange/from/USD/to/INR. You should get a valid response.

5. I am still not satisfied. I dont wan to give my service name everytime. Url kinda looks funny having currency-exchange-service twice. I wan to make the below url work

Url: http://localhost:8765/currency-exchange/from/USD/to/INR

To achieve this we need to add routes. We have to tell api-gateway everytime a url starts with /currency-exchange route it to CURRENCY-EXCHANGE-SERVICE in eureka load balancer. We can achieve this by adding a configuration class with custom routes.

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ApiGatewayConfiguration {

@Bean
public RouteLocator gatewayRouter(RouteLocatorBuilder builder) {
return builder
.routes()
.route(p-> p.path("/currency-exchange/**")
.uri("lb://CURRENCY-EXCHANGE-SERVICE")) // lb stands for load balancer
.route(p-> p.path("/currency-conversion/**")
.uri("lb://CURRENCY-CONVERSION-SERVICE"))
.route(p-> p.path("/currency-conversion-feign/**")
.uri("lb://CURRENCY-CONVERSION-SERVICE"))

//Example to show how headers and params can be added
.route(p-> p.path("/get/**")
.filters(f-> f.addRequestHeader("Username", "dummy_username")
.addRequestHeader("Password", "dummy_password")
.addRequestParameter("Param", "dummy_param"))
.uri("http://httpbin.org:80"))

/* Example to show how URL path can be altered
http://localhost:8765/currency-conversion/from/USD/to/INR/quantity/20
change to
http://localhost:8765/currency-conversion-new/from/USD/to/INR/quantity/20

Segment -> saying append what ever is after / using functional programming
*/
.route(p-> p.path("/currency-conversion-new/**")
.filters(f-> f.rewritePath("/currency-conversion-new/(?<segement>.*)", "/currency-conversion/${segement}"))
.uri("lb://CURRENCY-CONVERSION-SERVICE"))
.build();
}
}

Here we can

  • Match routes on any request attribute
  • Define Predicates and Filters- can add headers, attributes if needed
  • Integrates with spring cloud discovery client (Load Balancing)- define what eureka service to call for the url
  • Path Rewriting- rewrite the path and change the request url

Awesome! Now try Url: http://localhost:8765/currency-exchange/from/USD/to/INR. It should give a valid response.

6. We can future add a loggingFilter to log all the requests via api-gateway.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
* Filter to log every request that goes through api-gateway
*/
@Component
public class LoggingFilter implements GlobalFilter {

private Logger logger = LoggerFactory.getLogger(LoggingFilter.class);

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

logger.info("Path of the request requested -> {}",
exchange.getRequest().getPath());
return chain.filter(exchange);
}
}

Now everytime you hit http://localhost:8765/*. We will see the request url in the console.

Conclusion:

This is just a basic implementation of the api-gateway using spring cloud gateway. There is so much we can do with this.

References:

--

--