API Gateway Spring Boot: Setup, Options, Tradeoffs
"API gateway Spring Boot" usually means one of two things: you want to build a gateway using Spring Boot (typically with Spring Cloud Gateway), or you already have Spring Boot microservices and need a gateway to sit in front of them. Both are common, and the right answer depends on how many services you're routing to and whether you need this to scale past a side project.
This article covers the practical setup for Spring Cloud Gateway, the routing and filter patterns you'll actually use, and where a self-built gateway stops making sense — especially once external APIs like an LLM provider enter the picture.
Two different problems, same search term
If you have 3–15 Spring Boot services (auth, orders, inventory, etc.) and want one entry point that handles routing, auth, and rate limiting, Spring Cloud Gateway is the standard choice. It's built on Spring WebFlux, reactive, and integrates cleanly with Spring Boot's config system.
If instead you're trying to expose a single Spring Boot app's endpoints publicly with API keys, throttling, and usage tracking, you don't need a full gateway framework — you need an API management layer, which might be a managed product rather than code you maintain.
Setting up Spring Cloud Gateway
Add the dependency:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
Define routes in application.yml:
spring:
cloud:
gateway:
routes:
- id: orders-service
uri: http://localhost:8081
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- id: inventory-service
uri: lb://inventory-service
predicates:
- Path=/api/inventory/**
The lb:// prefix routes through Spring Cloud LoadBalancer if you're using service discovery (Eureka, Consul). The plain http:// URI is fine for a single fixed backend or local development.
You can define routes programmatically instead of in YAML when logic needs to be dynamic:
@Bean
public RouteLocator customRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("payments", r -> r.path("/api/payments/**")
.filters(f -> f.stripPrefix(1)
.addRequestHeader("X-Gateway", "spring-cloud-gateway"))
.uri("http://localhost:8082"))
.build();
}
Filters: auth, rate limiting, headers
Most of the real work in a gateway happens in filters, not routes. A JWT check as a global filter:
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
Rate limiting with Spring Cloud Gateway typically uses Redis-backed RequestRateLimiter:
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
This works well, but note the operational cost: you now own the Redis instance, the rate-limit config, and any drift between environments. For internal service-to-service routing that's a fair tradeoff. For public-facing APIs with paying customers, it starts to look like infrastructure you're maintaining instead of product you're shipping.
When to stop building and start buying
Spring Cloud Gateway is the right tool when the gateway's job is routing and cross-cutting concerns between your own services. It gets less appropriate when:
- You need per-customer API keys, billing, and usage dashboards — that's account management, not routing.
- You're proxying a third-party API (payments, AI, data providers) and don't want to re-implement their auth, retries, and streaming semantics yourself.
- Your team is small and every hour spent tuning Redis-backed rate limiters is an hour not spent on the product.
This is where managed API layers fit next to, not instead of, Spring Cloud Gateway. A common pattern: Spring Cloud Gateway handles routing between your internal services, and a route inside it forwards AI-related traffic to an external managed endpoint.
Example: routing AI calls through your gateway
If one of your Spring Boot services needs to call an LLM, you don't want raw provider credentials scattered across services. Route AI traffic through a dedicated upstream instead, the same way you'd route to any other backend:
- id: ai-service
uri: https://api.subtoapi.app
predicates:
- Path=/api/ai/**
filters:
- StripPrefix=2
- AddRequestHeader=Authorization, Bearer ${SUBTOAPI_KEY}
SubToAPI turns Claude access into a standard HTTPS API — sub_live_... keys, streaming, and usage metadata — so your Spring Boot gateway just proxies to a normal REST endpoint instead of embedding provider-specific SDK logic in every service that needs an AI call:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"messages": [{"role": "user", "content": "Summarize this ticket"}]
}'
Keeping this behind your own gateway route means auth, rate limiting, and internal service discovery stay consistent, while the actual AI provider integration — key rotation, streaming, tool use — is handled outside your codebase. Setup takes a few minutes; see the quickstart and full messages API reference if you want the request/response shapes before wiring it into a route.
Testing your gateway
Once routes are defined, verify with a plain curl call through the gateway port (default 8080 unless changed):
curl -i http://localhost:8080/api/orders/123 \
-H "Authorization: Bearer <token>"
Check that StripPrefix counts match your downstream service's actual path expectations — this is the single most common misconfiguration in Spring Cloud Gateway setups, and it fails silently as a 404 rather than a routing error.
Questions
Is Spring Cloud Gateway the same as Netflix Zuul? No. Zuul is Netflix's older, blocking servlet-based gateway, now largely legacy. Spring Cloud Gateway is reactive (WebFlux-based) and is the actively maintained option for new Spring Boot projects.
Do I need Spring Cloud Gateway if I only have one Spring Boot service? Usually not. A single service doesn't need routing between backends — add auth, rate limiting, and API keys directly in the app, or put it behind a managed layer for public API access.
Can Spring Cloud Gateway handle streaming responses, like from an LLM API? Yes, since it's built on reactive streams, but you need to configure it explicitly for server-sent events or chunked responses — see the provider's streaming docs for the expected format before wiring a route.