Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Spring Boot REST API Development

Spring Boot Basics

  • What is Spring Boot?
  • Dependency Injection
  • Building REST Controllers

Validation & Error Handling

  • Bean Validation with @Valid
  • Global Exception Handling

Spring Framework Internals

  • IoC Container Fundamentals
  • Bean Lifecycle In Detail
  • Component Scanning & Configuration
  • Auto-Configuration Mechanism
  • Spring AOP
  • @Transactional Deep Dive

Microservices Architecture

  • Monolith to Microservices Decomposition
  • API Gateway
  • Service Discovery
  • Inter-Service Communication Choices
  • Event-Driven Architecture Patterns
  • Messaging Technology Choices

Resilience Patterns

  • Configuration Management
  • Circuit Breaker Pattern
  • Retry & Backoff Strategies
  • Bulkhead & Rate Limiting
  • Timeout Strategy
  • Why Microservices Fail

Distributed Data & Consistency Patterns

  • Two-Phase Commit
  • Saga Pattern
  • Outbox Pattern
  • Eventual Consistency Design
  • CQRS Basics
  • CAP Theorem

Observability & Operations

  • Centralized Logging
  • Distributed Tracing
  • Metrics & Monitoring
  • Alerting Strategy
  • Health Checks

Payment Systems

  • Payment — Requirements
  • Payment — Core Flow
  • Payment — Idempotency Implementation
  • Payment — Failure Handling & Reconciliation
  • Payment — Security

Order Management System

  • OMS — Requirements
  • OMS — State Machine Design
Chaturmind
← Spring Boot REST API Development

Spring Boot Basics

  • What is Spring Boot?
  • Dependency Injection
  • Building REST Controllers

Validation & Error Handling

  • Bean Validation with @Valid
  • Global Exception Handling

Spring Framework Internals

  • IoC Container Fundamentals
  • Bean Lifecycle In Detail
  • Component Scanning & Configuration
  • Auto-Configuration Mechanism
  • Spring AOP
  • @Transactional Deep Dive

Microservices Architecture

  • Monolith to Microservices Decomposition
  • API Gateway
  • Service Discovery
  • Inter-Service Communication Choices
  • Event-Driven Architecture Patterns
  • Messaging Technology Choices

Resilience Patterns

  • Configuration Management
  • Circuit Breaker Pattern
  • Retry & Backoff Strategies
  • Bulkhead & Rate Limiting
  • Timeout Strategy
  • Why Microservices Fail

Distributed Data & Consistency Patterns

  • Two-Phase Commit
  • Saga Pattern
  • Outbox Pattern
  • Eventual Consistency Design
  • CQRS Basics
  • CAP Theorem

Observability & Operations

  • Centralized Logging
  • Distributed Tracing
  • Metrics & Monitoring
  • Alerting Strategy
  • Health Checks

Payment Systems

  • Payment — Requirements
  • Payment — Core Flow
  • Payment — Idempotency Implementation
  • Payment — Failure Handling & Reconciliation
  • Payment — Security

Order Management System

  • OMS — Requirements
  • OMS — State Machine Design
HomeLearnSpring BootSpring Boot REST API DevelopmentMicroservices Architecture
✓ FreeAdvanced· 9 min read

API Gateway

Core gateway responsibilities, the Backend-for-Frontend pattern, request aggregation/fan-out, Spring Cloud Gateway's route predicates and filters, and why the gateway itself needs to be highly available.

Published September 23, 2026


API Gateway

Core responsibilities

A single entry point sitting in front of a microservices architecture, handling cross-cutting concerns once instead of duplicating them in every service: routing (directing each request to the correct backend service), authentication (verifying identity before requests reach any service — see Security Filter Chain Architecture for the same chain-of-responsibility shape applied at the gateway level), rate limiting (see the Rate Limiter case for the algorithms, enforced here as one of the three enforcement-location options), and request/response transformation (adapting between what clients send and what backend services expect).

Backend-for-Frontend (BFF): different clients, different needs

[Mobile App] → [Mobile BFF]  → aggregates/trims responses for small screens, limited bandwidth
[Web App]    → [Web BFF]     → richer responses, more fields, different pagination defaults
                    ↓
         [Same underlying microservices]

Rather than one gateway serving every client type identically, BFF tailors the gateway layer itself per client — a mobile client typically wants smaller, more aggregated responses (fewer round trips over a potentially slow connection) while a web client might prefer more granular endpoints matching a richer UI's data needs. Each BFF is itself a thin service, not a full reimplementation — it composes and reshapes calls to the same underlying microservices differently per client type.

Request aggregation: composing one response from many services

@RestController
class ProductDetailBffController {
    @GetMapping("/bff/products/{id}")
    Mono<ProductDetailResponse> getProductDetail(@PathVariable String id) {
        Mono<Product> product = productServiceClient.getProduct(id);
        Mono<List<Review>> reviews = reviewServiceClient.getReviews(id);
        Mono<Inventory> inventory = inventoryServiceClient.getInventory(id);

        return Mono.zip(product, reviews, inventory)
            .map(tuple -> new ProductDetailResponse(tuple.getT1(), tuple.getT2(), tuple.getT3()));
            // fans out to 3 services in parallel, composes one response — see Inter-Service Communication Choices for Mono/Flux
    }
}

Without gateway-level aggregation, a mobile client would need to make three separate round-trips (to product, review, and inventory services individually) — aggregation moves that fan-out to the gateway (or a dedicated BFF), where it can run in parallel server-side (typically much lower latency, same datacenter) rather than sequentially from a client over a potentially slow network.

Spring Cloud Gateway basics

@Bean
RouteLocator customRoutes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("order-service", r -> r.path("/api/orders/**") // route predicate: match by path
            .filters(f -> f.stripPrefix(1).addRequestHeader("X-Gateway", "true")) // filters: transform the request
            .uri("lb://order-service")) // lb:// — resolved via service discovery, see Service Discovery
        .route("payment-service", r -> r.path("/api/payments/**")
            .filters(f -> f.circuitBreaker(c -> c.setName("paymentCB"))) // per-route resilience
            .uri("lb://payment-service"))
        .build();
}

Route predicates decide which requests a route matches (by path, header, method, and more). Filters modify the request/response as it passes through a matched route — stripping a path prefix, adding headers, applying a circuit breaker. Global filters apply across every route (e.g. a single authentication check applied to all traffic, rather than repeated per-route configuration) — the mechanism that makes "enforce auth once, for everything" practical rather than error-prone to configure per-route individually.

The gateway as a single point of failure

Every single request to every backend service flows through the gateway — if it goes down, the entire system becomes unreachable, regardless of how healthy the individual microservices behind it are. This makes the gateway's own high availability non-negotiable: multiple gateway instances behind a load balancer, health checks, and genuinely stateless gateway instances (so any instance can handle any request, enabling simple horizontal scaling and failover) are standard requirements, not optional hardening — a single-instance gateway defeats a large part of the resilience microservices decomposition was meant to buy in the first place.

Where to enforce cross-cutting concerns: gateway vs service, and why some belong in both

Authentication is a good gateway-level fit (reject unauthenticated requests before they cost any backend service compute). But authorization for fine-grained, resource-specific permissions ("can this user edit this specific document" — see Role-Based & Method-Level Authorization's custom permission evaluators) often needs data only the owning service has, making it a service-level concern. Rate limiting frequently belongs in both: a coarse global limit at the gateway (protecting the whole system from being overwhelmed) plus finer per-endpoint limits at individual services (protecting one specific expensive operation) — the same layered-defense idea covered in the Rate Limiter case study's enforcement-location tradeoffs.

Follow-up questions this topic invites — and their answers

Q: How does the gateway know which service instances are healthy and available for routing? A: Via the service discovery mechanism (see Service Discovery) — Spring Cloud Gateway's lb:// URI scheme resolves through a service registry (Eureka, Consul, or Kubernetes' own service resolution), which is exactly why the gateway itself doesn't hard-code backend service addresses.

Q: What happens to gateway-level rate limiting during a gateway instance restart/deploy? A: If rate-limit state is stored per-instance in memory, a restart resets that instance's counters — for correctness under a multi-instance, frequently-redeployed gateway, rate-limit state needs to live in a shared store (Redis, the same approach as the Rate Limiter case's distributed implementation), not in each gateway instance's local memory.

Q: Why use a circuit breaker filter on the payment-service route specifically? A: Payment is a common example of a downstream dependency where cascading failure is especially costly — if the payment service is slow or down, a circuit breaker at the gateway can fail fast (returning an error immediately) rather than letting requests queue up and exhaust gateway resources waiting on a downstream service that isn't going to respond in time.

Q: Could a BFF and the main API Gateway be the same service? A: For a simple system, yes — but as client-specific tailoring grows more complex, separating them lets the core gateway stay focused on routing/cross-cutting concerns (auth, rate limiting) while each BFF independently evolves its client-specific aggregation logic, avoiding one increasingly complex service trying to serve both roles at once.

Previous

Monolith to Microservices Decomposition

Next

Service Discovery

AI Tutor

Lesson: API Gateway

Quick actions

AI responses can be inaccurate. Verify critical information.