Building an API Gateway using Spring Boot 3 Cloud Gateway and Eureka
As microservices architecture becomes increasingly popular, the need for an API Gateway to manage and secure communication between services has become more important than ever. In this blog post, we will explore how to implement an API Gateway using Spring Cloud Gateway and service auto registry using Netflix Eureka. We will cover the key features of Spring Cloud Gateway, how it works, and step-by-step instructions on how to set it up in your microservices architecture.
Spring Cloud Gateway
Spring Cloud Gateway is an open-source API Gateway framework built on top of the Spring Boot and Spring WebFlux frameworks. It provides a way to route, filter, and transform HTTP requests before they reach the backend services.
It is designed to be highly configurable and flexible, making it a good choice for complex API architectures. Some of its key features include:
- Routing: Spring Cloud Gateway provides a powerful routing engine that can route requests based on various criteria such as the request path, host header, or request method.
- Filters: It supports a wide range of filters that can be used to modify the request or response, add headers, perform authentication and authorization, and more.
- Load balancing: Spring Cloud Gateway integrates with various load balancing technologies such as Ribbon, allowing it to distribute requests across multiple instances of a service.
- Caching: It can cache responses from backend services, reducing the load on the services and improving performance.
- Circuit breaking: Spring Cloud Gateway includes support for circuit breaking, which can help prevent cascading failures in the system by failing fast when a service is unavailable.
Overall, Spring Cloud Gateway is a powerful and flexible API Gateway framework that provides a way to build scalable and secure API architectures.
When implementing any new architecture, it is important to understand the tradeoffs of those decisions. Some of the benefits of implementing an API Gateway are:
- Scalability: API Gateway acts as a centralized entry point for all the API requests, which makes it easier to scale the system horizontally by adding more instances of the API Gateway.
- Security: API Gateway provides a layer of security by acting as a single point of control for all the API requests, allowing for the implementation of security measures like authentication, authorization, rate limiting, and encryption.
- Ease of use: API Gateway makes it easier for developers to build, deploy, and manage APIs by providing a simple and consistent interface.
- Flexibility: API Gateway allows for the implementation of custom logic, such as request/response transformations, caching, and routing.
- Monitoring and analytics: API Gateway provides detailed insights into the API traffic, which makes it easier to monitor the system and identify any issues.
Some issues to be aware of when implementing an API Gateway are:
- Complexity: API Gateway adds an additional layer of complexity to the architecture, which can increase the development time and cost.
- Single point of failure: API Gateway is a critical component of the system, and any issues with it can lead to downtime and impact the entire system.
- Latency: API Gateway can introduce additional latency into the system, as all the API requests have to go through it.
- Vendor lock-in: API Gateway is typically provided by a single vendor, which can make it harder to switch to a different provider.
- Limited functionality: API Gateway provides a limited set of features, and in some cases, it may not be sufficient to meet the needs of the system.
Implementation Steps
Now that we understand the basics of Spring cloud and some of the tradeoffs we should consider, let's implement a basic API Gateway. In this tutorial, we are going to build two restful microservices, a Eureka service registry, and an API Gateway service as modeled above.
First, we are going to create microservices that will interact with our API Gateway. We are going to create 2 services that will be nearly identical except for the project name, request mapping, and server port. You can create a new Spring Boot project using Spring Initializer or you can skip this step and clone the existing services I have here.
If starting from scratch ensure you have the following dependencies in your Gradle build file:
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
Annotate your Main class with @EnableGateway to enable Spring Cloud Gateway in your application and @SpringBootApplication for Spring boot.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class CustomerServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerServiceApplication.class, args);
}
}
Next, we will create a controller and a route.
@RestController
@RequestMapping("/customer")
public class CustomerController {
@GetMapping("/{id}")
public String getCustomer(@PathVariable String id){
return String.format("Get customer with id %s", id);
}
}
Finally, we need to tell the service how to register with the Eureka server. In application.properties add the following configuration.
spring.application.name=customer-service
server.port=8082
eureka.client.service-url.defaultZone=http://localhost:8083/eureka
Now let's create our API Gateway and Eureka services.
Start by creating a new project using Spring Inititializer and the dependency for Netflix Eureka server.
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
Annotate the main class with @SpringBootApplication and @EnableEurekaServer to set up the spring boot application with the Eureka server enabled.
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Update the application properties to configure the Eureka server to run on port 8083.
spring.application.name=eureka-discovery-service
server.port=8083
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.service-url.defaultZone=http://localhost:8083/eureka
Now if we run our microservices and the eureka service we should see that our services have auto-registered with the eureka service by navigating to localhost:8083.
Finally, we are going to create the API Gateway service. Start by creating a new project using Spring Inititializer containing the Cloud Gateway, Eureka Client, and Actuator dependencies.
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
As before we are going to annotate our main class with the @SpringBootApplication and @EnableDiscoveryClient annotations.
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
We also need a Spring Configuration class that creates a bean that defines the routes in the Gateway. When using the service registry the Gateway does not need the actual path to each service, just the service name.
@Configuration
public class SpringCloudConfiguration {
@Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("invoiceModule", r -> r.path("/invoice/**")
.uri("lb://invoice-service")
)
.route("customerModule", r -> r.path("/customer/**")
.uri("lb://customer-service")
)
.build();
}
}
Finally, we need to configure the application properties to configure Eureka and enable Actuator.
server.port=8080
spring.application.name=gateway-service
eureka.client.service-url.defaultZone=http://localhost:8083/eureka
management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=*
Running all four services we should now be able to access our two microservices through localhost:8080 and we should see the two services and the gateway services registered with Eureka.
If you access the customer endpoint directly localhost:8082/customer/{id} or through the API gateway localhost:8080/customer/{id} you will get the same response. Notice that neither the client nor the API gateway needs to know the service's actual location. With tools like Eureka, services can be added or removed from the system without requiring any changes to the API gateway configuration, and routing logic is automatically adjusted to ensure that requests are always routed to available services. We can also view all of the routes registered on the API gateway by checking the actuator endpoint, http://localhost:8080/actuator/gateway/routes.
[
{
"predicate": "Paths: [/invoice/**], match trailing slash: true",
"route_id": "invoiceModule",
"filters": [],
"uri": "lb://invoice-service",
"order": 0
},
{
"predicate": "Paths: [/customer/**], match trailing slash: true",
"route_id": "customerModule",
"filters": [],
"uri": "lb://customer-service",
"order": 0
}
]
In this article, I have described the high-level architecture of creating an API Gateway using Spring Boot 3 with some basic routing and service auto registration using Netflix Eureka. A complete demo can be found here https://github.com/dheins/Api-Gateway.
In future articles, I will dive deeper into some of the key features and benefits that implementing an API Gateway in Spring boot offers such as filtering, caching, circuit breaking, security, monitoring, and scalability.