Spring Boot API Gateway for Microservices

An API gateway serves as the single entry point for clients to interact with multiple microservices. Instead of connecting directly to every service, clients communicate through the gateway, which handles routing, authentication, rate limiting, and more. Using Spring Boot and Spring Cloud Gateway, you can create a reactive, high-performance gateway that supports dynamic routing, security, and resilience.
Key Takeaways:
- Core Functions: Routing, authentication, rate limiting, circuit breaking, and monitoring.
- Why Spring Boot?: Leverages Spring Cloud’s reactive, non-blocking architecture for high-concurrency workloads.
- Setup Requirements: Java 17+, Maven/Gradle, Netty runtime (not servlet-based).
- Configuration: Define routes in
application.yamlor programmatically with Java. - Advanced Features: Service discovery (e.g., Eureka), rate limiting (Redis-backed), and monitoring via Spring Boot Actuator.
Spring Cloud Gateway makes it easier to manage microservices communication while maintaining a clean, secure, and scalable architecture.
Setting Up Your Spring Boot API Gateway

To get started, you’ll need Java 17 (up to Java 21), Apache Maven 3.6.3+ or Gradle 7.5+, and a Netty runtime rather than a servlet-based one. This is because Spring Boot’s API Gateway relies on Netty, provided by Spring Boot and Spring Webflux, making it incompatible with traditional servlet containers like Tomcat or WAR deployments.
“Spring Cloud Gateway requires the Netty runtime provided by Spring Boot and Spring Webflux. It does not work in a traditional Servlet Container or when built as a WAR.” – Spring Cloud Gateway Documentation
Prerequisites for Implementation
Before jumping in, make sure your setup meets the technical requirements. The gateway is built on Spring Boot 3.x and Spring Framework 6.x. You’ll also need to be comfortable working with YAML configuration or Java DSL for route definitions. Since the gateway uses a non-blocking, reactive model, some familiarity with reactive programming is helpful.
Planning to add service discovery? Tools like Eureka or Consul can be integrated seamlessly. For dependencies, include spring-cloud-starter-gateway for the gateway’s core functionality, spring-boot-starter-actuator for monitoring, and spring-cloud-starter-circuitbreaker-reactor-resilience4j to add resilience to your setup.
Once your environment is ready, you can move on to creating your Spring Boot project.
Creating a Spring Boot Project
Head over to Spring Initializr at start.spring.io and select the “Reactive Gateway” option. This ensures the necessary non-blocking dependencies are included right from the start. Choose Maven or Gradle as your build tool, and don’t forget to add the Spring Cloud Bill of Materials (BOM) to your pom.xml or build.gradle for version compatibility across Spring Cloud modules. Package your application as an executable JAR, and you’re ready to define your routes.
Configuring Routes in application.yaml
With your project set up, it’s time to configure routes in the application.yaml file. Routes go under the spring.cloud.gateway.routes property. Each route should include:
- An ID to identify the route
- A destination URI (e.g.,
http://localhost:8081for static routing orlb://user-servicefor load-balanced services) - A set of predicates to match conditions like paths or methods
- Filters to modify requests or responses
For simple cases, you can use a shortcut format like Path=/api/**. For more complex scenarios, use a detailed format specifying name and args. For example, if your gateway routes requests from /api/users/** to a microservice expecting /users/**, you can use the StripPrefix=1 filter to remove the first segment of the path.
You can monitor your routes using the Spring Boot Actuator endpoint at /actuator/gateway/routes. To refresh routes dynamically without restarting the application, send a POST request to /actuator/gateway/refresh.
Testing and Validating the API Gateway
Once your Spring Boot API Gateway is configured, thorough testing is key to ensuring smooth communication between the gateway and the backend services. This involves checking that the gateway routes requests correctly. You can test manually using tools like curl or Postman, and automate tests using @SpringBootTest and WebTestClient for more comprehensive coverage.
Start by verifying direct access to your microservice before involving the gateway. For example, if your service is running on port 9091, you can test it directly by calling http://localhost:9091/api/v1/customers. This helps isolate whether issues stem from the microservice itself or the gateway’s routing.
For automated testing, use @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) with WebTestClient. This allows you to simulate real HTTP requests and validate response statuses and payloads. Additionally, you can use WireMock with @AutoConfigureWireMock to mock downstream services and confirm the gateway’s behaviour.
To simulate real-world scenarios, create a simple microservice to generate test traffic and validate routing.
Building a Sample Microservice
A sample microservice can make testing much easier. Create a new Spring Boot project using spring-boot-starter-web and add a basic @RestController with a GET endpoint that returns a recognisable response. For instance, a microservice named DEMO-SERVICE running on port 8081 might expose an endpoint like /users/info, which returns a JSON response such as:
{ "service": "demo-service", "status": "active" } This straightforward setup allows you to confirm that the gateway is routing requests to the correct service.
Verifying Gateway Routing
Once your microservice is responding as expected, test the gateway’s routing. First, confirm the microservice works by directly calling its endpoint, e.g., http://localhost:8081/users/info. Next, test the same endpoint through the gateway, for example, http://localhost:8080/api/users/info, assuming your gateway runs on port 8080 and the route is configured with /api/users/**. If you’ve applied a StripPrefix=1 filter, the gateway should strip the /api segment before forwarding the request. You can double-check your active routes using the Actuator endpoint at /actuator/gateway/routes to ensure they match your configuration.
For advanced routing scenarios, test headers or cookies by including the necessary metadata in your requests. For example, you can use the following command to test header-based routing:
curl --header 'Host: www.example.com' http://localhost:8080/api/users/info For weight-based routing, configure primary and canary versions of a service with a split like 80/20 and validate the distribution.
If routes don’t behave as intended, investigate for route shadowing. This occurs when broader routes with wildcards intercept requests meant for more specific routes. To avoid this, use the order property in your configuration to ensure that specific routes take precedence.
Advanced API Gateway Configuration

YAML vs Java Bean API Gateway Route Configuration Comparison
Once you’ve set up basic routing, you can enhance your gateway with advanced configurations. These settings play a key role in streamlining communication between clients and backend services within a microservices architecture.
YAML vs Java Bean Route Definitions
Routes can be defined using either YAML or Java Bean configurations, each offering distinct advantages.
- YAML Configuration: Routes are declared with Spring Boot’s
@ConfigurationPropertiesand thePropertiesRouteDefinitionLocator. This approach is ideal for straightforward use cases where operations teams can tweak settings without needing to recompile the code. However, YAML predicates are limited to logicalANDcombinations, which restricts flexibility. - Java Bean Configuration: Using
RouteLocatorBuilder, this method allows for programmatic route definitions. It supports more complex logic with operators likeand(),or(), andnegate(), making it a better fit for advanced production scenarios. Java configurations also provide type safety, IDE support, and make it easier to test routing logic with tools like WireMock.
| Feature | YAML Configuration | Java Bean Configuration |
|---|---|---|
| Method | Declarative (application.yml) | Programmatic (Fluent API) |
| Logic Complexity | Limited to AND | Supports multiple logical operators |
| Maintainability | Simple changes without recompilation | Ideal for complex logic with type safety |
| Flexibility | Built-in factories only | Allows custom predicate assertions |
| Use Case | Static microservice routing | Dynamic, logic-heavy routing |
For static routes, YAML is a good choice, especially when managed externally through tools like Spring Cloud Config. On the other hand, Java Bean definitions are better for routes requiring conditional logic or pulling configurations from sources like Redis or MongoDB.
Implementing Rate Limiting and Monitoring
Rate limiting helps manage traffic spikes and prevent misuse. Spring Cloud Gateway uses the RequestRateLimiter filter, which relies on a Redis-backed Token Bucket algorithm. To enable this, include the spring-boot-starter-data-redis-reactive dependency in your project.
“Rate limiting is a technique to control the rate by which an API or a service is consumed.”
– Haytham Mohamed, Engineering, Spring Blog
You’ll also need to define a KeyResolver bean to group requests – for example, by user ID, IP address, or Principal name. If no key is resolved, the gateway will block the request by default. Adjust the replenishRate and burstCapacity values to control traffic flow. Set both values equally for steady traffic, or allow a higher burst capacity to handle spikes. If limits are exceeded, the gateway responds with HTTP 429.
For monitoring, Spring Boot Actuator’s gateway endpoints provide visibility into active routes and filter configurations. Use /actuator/gateway/routes to view all routes or /actuator/gateway/refresh to clear the route cache dynamically.
Integrating Service Discovery
Service discovery removes the need for hardcoded backend URIs by automatically creating routes based on services registered with a DiscoveryClient (e.g., Eureka, Consul, or Zookeeper). To enable this, set spring.cloud.gateway.discovery.locator.enabled=true in your configuration. The gateway will then generate default predicates (like /serviceId/**) and filters (such as RewritePath) for each service.
Using the lb://service-name protocol allows integration with Spring Cloud LoadBalancer, distributing traffic across multiple instances seamlessly. As microservice instances scale up or down, the service registry updates automatically, and the gateway adjusts routing without requiring a restart. If you’re using Netflix Eureka, which defaults to uppercase service IDs, set spring.cloud.gateway.discovery.locator.lower-case-service-id=true to ensure URL paths like /my-service/** resolve correctly. You can also filter exposed services with the spring.cloud.gateway.discovery.locator.include-expression property and verify routes via the /actuator/gateway/routes endpoint.
Conclusion
Summary of Main Points
An API gateway acts as the entry point to your microservices, simplifying tasks like authentication, routing, and traffic management. Spring Cloud Gateway, built on the reactive and non-blocking framework of Spring WebFlux and Project Reactor, is designed to efficiently manage high-concurrency requests.
This guide walked through setting up a Spring Boot project, configuring routes via application.yaml, and testing with sample microservices. It also touched on advanced features like programmatic route definitions and integrating service discovery for dynamic routing and scaling.
Rate limiting, such as the Redis-based approach, protects backend services from sudden traffic spikes. For example, in January 2026, OneUptime demonstrated a configuration using a replenishRate of 10 and a burstCapacity of 20, utilising IP-based bucketing. Meanwhile, service discovery with Eureka enables dynamic routing by leveraging the lb:// protocol, removing the need for hardcoded URLs as services scale.
These strategies lay the groundwork for a reliable microservices framework. By applying these principles, and with expert support from Tucanoo Solutions Ltd, you can enhance and secure your API gateway using tailored Spring Boot solutions and cloud integration.
FAQs
Why can’t Spring Cloud Gateway run on Tomcat or as a WAR?
Spring Cloud Gateway doesn’t work on Tomcat or as a WAR file because it is built using Spring Boot’s reactive WebFlux framework. This framework depends on the Netty runtime, which is not compatible with traditional Servlet containers like Tomcat. These containers are designed to support Spring MVC and do not align with the reactive programming model.
When should I define routes in YAML vs Java?
Defining routes in YAML (like application.yml) works perfectly for configurations that are static or tied to specific environments. It’s straightforward and allows for quick adjustments without needing to touch the code. On the other hand, using Java is the way to go when you need dynamic routing or logic-based decisions. This method supports runtime conditions, integrates with external data sources, and handles more complex scenarios.
Spring Cloud Gateway supports both YAML and Java-based configurations, so the choice largely depends on the complexity and requirements of your project. YAML is great for simplicity, while Java offers the flexibility needed for advanced setups.
How do I enable Redis rate limiting in Spring Cloud Gateway?
To set up Redis-based rate limiting in Spring Cloud Gateway, you’ll need to use the RequestRateLimiter filter with Redis as the storage backend. Start by configuring Redis in your application settings. Then, define rate-limiting properties such as replenishRate (the number of requests allowed per second) and burstCapacity (the maximum number of requests that can be queued) within your route configuration.
If you need custom behaviour, make sure to create a RedisRateLimiter bean. This setup helps control the flow of requests and automatically rejects any that exceed the defined limits with HTTP 429 (Too Many Requests) responses.
