Skip to main content

7.3 API Gateway with Spring Cloud Gateway

← Back to Chapter 7: Microservices & Spring Cloud

An API Gateway acts as the single public entry point for all client requests. It shields internal microservice topography, offloads cross-cutting edge concerns (authentication, rate limiting, CORS), and routes traffic dynamically.


1. Gateway Architecture & Core Concepts​

Built natively on Project Reactor and Netty (non-blocking, reactive runtime), Spring Cloud Gateway provides superior throughput compared to legacy blocking reverse proxies.

[ Mobile / Web Clients ]
│ https://api.codewithsarvy.dev
▼
┌─────────────────────────────────────────────────────────────┐
│ Spring Cloud Gateway │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Route: ID, Destination URI, Predicates, Filters │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ Predicates: Path=/api/orders/**, Header=X-Client-Type │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ Gateway Filters: AddRequestHeader, RateLimiter, Token │ │
│ └─────────────────────────────────────────────────────────┘ │
└──────────────┬───────────────────────────┬──────────────────┘
│ lb://order-service │ lb://payment-service
▼ ▼
[ Order Service Pods ] [ Payment Service Pods ]

2. Declarative Route Configuration (application.yml)​

spring:
cloud:
gateway:
discovery:
locator:
enabled: true # Automatically route using Eureka service names
routes:
- id: order-service-route
uri: lb://order-service # Dynamic client-side load balanced URI!
predicates:
- Path=/api/v1/orders/**
filters:
- AddRequestHeader=X-Gateway-Source, CodeWithSarvyGateway
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20

- id: payment-service-route
uri: lb://payment-service
predicates:
- Path=/api/v1/payments/**

3. Custom Global Authentication Gateway Filter​

Authenticate JWT tokens at the gateway boundary so invalid requests are rejected before ever reaching internal microservice clusters:

@Component
public class AuthenticationGatewayFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();

if (isSecured(request)) {
if (!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}

String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
if (!isValidJwt(token)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
}
return chain.filter(exchange);
}

@Override
public int getOrder() { return -1; } // High precedence
}