Spring BootAPI Gateway
Spring Boot

API Gateway

An API Gateway is the single entry point for all client requests in a microservices architecture. It sits between clients and backend services, handling cross-cutting concerns such as routing, authentication, rate limiting, SSL termination, and request/response transformation. Clients talk to one stable URL; the gateway fans out to whichever internal services are needed.

Why an API Gateway Is Needed

Without a gateway, clients must know the address of every microservice, handle partial failures across multiple calls, and repeat cross-cutting logic (auth, logging, CORS) in every service. The gateway centralises all of that. It also decouples clients from the internal service topology β€” services can be split, renamed, or relocated without any client change.
Java
// ── Without API Gateway: ─────────────────────────────────────────────
//
//  Mobile App
//    β”œβ”€β”€ GET http://user-service:8081/api/users/1
//    β”œβ”€β”€ GET http://order-service:8082/api/orders?userId=1
//    └── GET http://payment-service:8083/api/payments?userId=1
//
//  Problems:
//  β€’ Client must know 3 different host:port combinations
//  β€’ Each service must implement auth, CORS, rate limiting independently
//  β€’ Any service rename/move requires a client update
//  β€’ Mobile app makes 3 round trips β€” slow on poor connections
//  β€’ No single place to enforce security policies

// ── With API Gateway: ─────────────────────────────────────────────────
//
//  Mobile App
//    └── All requests β†’ http://api.example.com  (gateway)
//            β”‚
//            β”œβ”€β”€ /api/users/**    ──▢  user-service:8081
//            β”œβ”€β”€ /api/orders/**   ──▢  order-service:8082
//            └── /api/payments/** ──▢  payment-service:8083
//
//  Benefits:
//  + Client knows exactly ONE address
//  + Auth/CORS/rate limiting enforced ONCE at the gateway
//  + Internal topology hidden β€” move services without client changes
//  + Gateway can aggregate multiple service calls into one response
//  + SSL terminated at the gateway β€” internal traffic can be plain HTTP

// ── What a gateway handles on every request: ─────────────────────────
//
//  Inbound request
//       β”‚
//       β–Ό
//  [1] SSL Termination    β€” decrypt HTTPS, forward HTTP internally
//  [2] Authentication     β€” validate JWT / API key
//  [3] Rate Limiting      β€” reject if client exceeds quota
//  [4] Request Logging    β€” log method, path, client IP, latency
//  [5] Routing            β€” which service handles this path?
//  [6] Load Balancing     β€” which instance of that service?
//  [7] Request Transform  β€” add/remove headers, rewrite paths
//       β”‚
//       β–Ό
//  Downstream Service
//       β”‚
//       β–Ό
//  [8] Response Transform β€” add CORS headers, strip internal headers
//  [9] Circuit Breaking   β€” return fallback if service is down
//       β”‚
//       β–Ό
//  Client

Gateway Responsibilities

A gateway is responsible for a well-defined set of cross-cutting concerns. Each concern that the gateway handles is one less thing every downstream service needs to implement independently. The most critical are routing, authentication, and rate limiting.
Java
// ── 1. ROUTING β€” path-based and header-based: ────────────────────────
//
//  /api/users/**    β†’ user-service
//  /api/orders/**   β†’ order-service
//  /api/v2/**       β†’ new version of services (canary routing)
//  Header: X-Beta: true β†’ beta-service  (header-based routing)

// ── 2. AUTHENTICATION β€” centralised JWT validation: ───────────────────
//
//  Gateway validates the JWT on every request.
//  If valid β†’ strip the token, forward user identity in a header.
//  If invalid β†’ return 401 immediately; downstream never sees the request.
//
//  Downstream services trust the gateway:
//    X-User-Id: 42
//    X-User-Role: ADMIN
//  No need for each service to re-validate the JWT.

// ── 3. RATE LIMITING β€” protect services from overload: ────────────────
//
//  Per-client limits (by IP or API key):
//    Free tier:    100 requests / minute
//    Pro tier:   1,000 requests / minute
//    Internal:  unlimited
//
//  Exceeded β†’ 429 Too Many Requests (before the request reaches services)

// ── 4. REQUEST / RESPONSE TRANSFORMATION: ────────────────────────────
//
//  Add upstream headers:
//    X-Request-Id: uuid         (for distributed tracing)
//    X-Gateway-Source: gateway  (so services know request came via gateway)
//    X-Real-IP: <client IP>
//
//  Remove sensitive response headers before returning to client:
//    Remove: X-Powered-By, Server, X-Internal-Service-Version

// ── 5. AGGREGATION (Backend for Frontend pattern): ────────────────────
//
//  Mobile client needs: user profile + recent orders + payment status
//  Instead of 3 client round trips, the gateway (or a BFF service) calls
//  all three services and returns a single merged response.
//
//  Client:  GET /api/dashboard/42
//  Gateway calls:
//    β†’ user-service/api/users/42
//    β†’ order-service/api/orders?userId=42&limit=5
//    β†’ payment-service/api/payments?userId=42&status=PENDING
//  Returns: { user: {...}, orders: [...], pendingPayments: [...] }

// ── 6. CIRCUIT BREAKING β€” fallback when a service is down: ────────────
//
//  payment-service is returning 503:
//    Without circuit breaker β†’ gateway waits, threads pile up, gateway hangs
//    With circuit breaker    β†’ gateway returns cached/default response fast
//    Fallback response: { "paymentStatus": "unavailable" }

API Gateway vs Load Balancer vs Reverse Proxy

These three components are often confused. A reverse proxy (nginx, HAProxy) forwards traffic and handles SSL. A load balancer distributes traffic across identical instances of the same service. An API gateway does both of those β€” and adds application-layer intelligence such as authentication, routing by path, request transformation, and circuit breaking.
Java
// ── Comparison: ───────────────────────────────────────────────────────
//
// β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
// β”‚ Feature            β”‚ Reverse Proxy β”‚ Load Balancerβ”‚ API Gateway  β”‚
// β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
// β”‚ SSL termination    β”‚ βœ“             β”‚ βœ“            β”‚ βœ“            β”‚
// β”‚ Forward to backend β”‚ βœ“             β”‚ βœ“            β”‚ βœ“            β”‚
// β”‚ Distribute load    β”‚ basic         β”‚ βœ“            β”‚ βœ“            β”‚
// β”‚ Path-based routing β”‚ limited       β”‚ βœ—            β”‚ βœ“            β”‚
// β”‚ Header-based route β”‚ βœ—             β”‚ βœ—            β”‚ βœ“            β”‚
// β”‚ Authentication     β”‚ βœ—             β”‚ βœ—            β”‚ βœ“            β”‚
// β”‚ Rate limiting      β”‚ βœ—             β”‚ βœ—            β”‚ βœ“            β”‚
// β”‚ Request transform  β”‚ βœ—             β”‚ βœ—            β”‚ βœ“            β”‚
// β”‚ Circuit breaking   β”‚ βœ—             β”‚ βœ—            β”‚ βœ“            β”‚
// β”‚ Service discovery  β”‚ βœ—             β”‚ βœ—            β”‚ βœ“            β”‚
// β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
//
// Real-world stack β€” these are often layered:
//
//  Internet
//     ↓
//  Cloud Load Balancer (AWS ALB / GCP LB)  ← distributes across gateway pods
//     ↓
//  API Gateway (Spring Cloud Gateway)      ← routing, auth, rate limiting
//     ↓
//  Microservices                           ← business logic

// ── Gateway types: ───────────────────────────────────────────────────
//
// Self-hosted (code in your repo):
//   Spring Cloud Gateway   β†’ Java / Spring Boot
//   Netflix Zuul 2         β†’ Java (legacy, mostly replaced by SCG)
//
// Infrastructure / cloud-managed:
//   AWS API Gateway        β†’ managed, serverless-friendly
//   Kong                   β†’ plugin-based, Lua / Go
//   NGINX API Gateway      β†’ high-performance C-based proxy
//   Traefik                β†’ cloud-native, auto-discovers Docker/K8s services

Backend for Frontend (BFF) Pattern

A single gateway serving all clients can become bloated with client-specific logic. The Backend for Frontend pattern uses a dedicated gateway per client type β€” one for the web app, one for mobile, one for third-party partners. Each BFF aggregates and shapes data for its specific client without burdening other clients or downstream services.
Java
// ── Single gateway problem: ───────────────────────────────────────────
//
//  Web app   β†’ needs full user profile, all order fields, charts data
//  Mobile app→ needs minimal profile, 3 most recent orders, no charts
//  Partner   β†’ needs only order status and tracking number (no user PII)
//
//  One gateway trying to serve all three β†’ complex conditional logic,
//  over-fetching on mobile, under-fetching for web.

// ── BFF pattern β€” one gateway per client: ─────────────────────────────
//
//                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
//  Browser ──────────▢│  Web BFF :8080  β”‚
//                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
//                              β”‚ calls user-service, order-service,
//                              β”‚ analytics-service, returns rich payload
//
//                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
//  Mobile App ────────▢│ Mobile BFF :8081β”‚
//                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
//                              β”‚ calls same services, returns
//                              β”‚ trimmed/compressed payload
//
//                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
//  Partner API ───────▢│Partner BFF :8082β”‚
//                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
//                              β”‚ exposes only public-safe fields,
//                              β”‚ enforces partner-specific rate limits
//
//  All three BFFs call the same downstream microservices.
//  Each shapes the response for its specific consumer.

// ── Mobile BFF example (returns trimmed order list): ─────────────────
@RestController
@RequiredArgsConstructor
@RequestMapping("/mobile/api")
public class MobileBffController {

    private final UserClient userClient;
    private final OrderClient orderClient;

    @GetMapping("/dashboard/{userId}")
    public MobileDashboardResponse getDashboard(@PathVariable Long userId) {
        UserResponse user   = userClient.findById(userId);
        List<OrderResponse> orders = orderClient.findRecent(userId, 3);

        // Shape response specifically for mobile β€” minimal payload:
        return MobileDashboardResponse.builder()
            .userName(user.getFirstName())       // not full profile
            .avatarUrl(user.getAvatarUrl())
            .recentOrders(orders.stream()
                .map(o -> new OrderSummary(o.getId(), o.getStatus()))
                .toList())                       // status only, no line items
            .build();
    }
}