System Design

API Gateway

A single entry point for a set of services, centralizing authentication, routing, rate limiting, logging, and request transformation — at the cost of introducing a critical component that needs high availability and discipline to avoid becoming a god object.

Intent

The API Gateway is the intermediary between external clients and a system's internal services, offering a single entry address that centralizes cross-cutting concerns. Instead of each service implementing authentication, rate limiting, and logging individually, the Gateway handles those concerns once, before forwarding the request to the responsible service.

The pattern is especially valuable in microservices architectures, where dozens or hundreds of services would need redundant implementations of those features if there were no centralizing point. External clients see only one address; the internal complexity stays encapsulated behind the Gateway.

Problem

In a microservices architecture, each service is an independent process with its own network address. Exposing all of them directly to clients creates a set of problems that are hard to solve individually in each service:

  • Duplicated authentication: each service would need to validate JWT tokens or API keys. Any inconsistency in the implementation creates security gaps. Keeping the logic up to date across twenty services is operationally unfeasible.
  • Client coupling to internal topology: if the client knows that /users lives on host A and /orders on host B, any internal reorganization breaks the clients. The client shouldn't know the system's internal topology.
  • Inconsistent rate limiting: protecting each service individually requires repeated implementation and configuration. A service without rate limiting is a vector for abuse or overload-induced downtime.
  • Fragmented observability: logs scattered across dozens of uncorrelated services make it harder to diagnose problems that span multiple services.
  • Different needs across distinct clients: a mobile app needs compact responses; a web dashboard needs aggregated data; a third-party integration follows a fixed contract. Each client has different needs for the same data.

How it works

Request flow

  Client (browser, mobile app, partner)
        │
        ▼
  ┌─────────────────────────────────────────────┐
  │               API Gateway                   │
  │                                             │
  │  1. AuthN/AuthZ (JWT/API Key)               │
  │  2. Rate Limiting                           │
  │  3. SSL Termination                         │
  │  4. Routing by path/method                  │
  │  5. Request/response transformation         │
  │  6. Logging and metrics                     │
  └──────────────┬──────────────────────────────┘
                 │ routes to the correct service
        ┌────────┼────────────┐
        ▼        ▼            ▼
  ┌──────────┐ ┌──────────┐ ┌──────────┐
  │  User    │ │  Order   │ │ Payment  │
  │ Service  │ │ Service  │ │ Service  │
  └──────────┘ └──────────┘ └──────────┘

Typical responsibilities

  • Routing: maps external paths to internal services. /users/* goes to the User Service; /orders/* goes to the Order Service. Internal services don't need to know they're exposed under those paths.
  • Authentication and Authorization: validates JWT, API Key, or session before forwarding the request. Internal services trust that the Gateway already validated identity — they receive only the user context (e.g., user ID in an internal header), without needing to implement authentication.
  • Rate Limiting: limits the number of requests per IP, per authenticated user, or per API key within a time window. Protects downstream services from accidental or intentional overload.
  • SSL Termination: the Gateway decrypts HTTPS and forwards plain HTTP to internal services. Internal communication stays on a private network, simplifying certificate management (only the Gateway needs a public certificate).
  • Request and response transformation: can add headers, convert formats (e.g., external REST to internal gRPC), and aggregate responses from multiple services into a single response to the client.
  • Logging and Observability: a central point to record every request with a correlation ID, making it easier to trace a request across multiple services.
  • Circuit Breaker: can encapsulate resilience logic for downstream services — if a service is failing, the Gateway can return a fallback response without propagating the instability to the client.

BFF — Backend for Frontend

The BFF pattern is a variation where each client type has its own specialized Gateway with transformations tailored to its needs:

  ┌─────────────┐   ┌─────────────┐   ┌──────────────────┐
  │  Mobile App │   │  Web App    │   │  Partners (B2B)  │
  └──────┬──────┘   └──────┬──────┘   └────────┬─────────┘
         │                 │                    │
         ▼                 ▼                    ▼
  ┌─────────────┐   ┌─────────────┐   ┌──────────────────┐
  │  BFF Mobile │   │  BFF Web    │   │  BFF B2B         │
  │  (compact)  │   │ (aggregated)│   │ (fixed contract) │
  └──────┬──────┘   └──────┬──────┘   └────────┬─────────┘
         └─────────────────┼────────────────────┘
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │  User    │ │  Order   │ │ Payment  │
        │ Service  │ │ Service  │ │ Service  │
        └──────────┘ └──────────┘ └──────────┘

The BFF solves the problem of clients with distinct needs: the mobile app needs smaller payloads; the web dashboard needs data aggregated from multiple services in one call; B2B partners have fixed, stable API contracts. Each BFF adapts the same backend for the client it serves, without internal services needing to know the different formats.

API Gateway vs Reverse Proxy

  REVERSE PROXY (nginx, HAProxy, Traefik)
  ─────────────────────────────────────────
  Routes requests and does load balancing.
  No business or application logic.
  Configured by static rules (paths, hosts).
  High performance, low overhead latency.

  API GATEWAY (Kong, AWS API Gateway, Apigee)
  ─────────────────────────────────────────
  Reverse proxy + authentication + rate limiting
  + transformations + centralized logging.
  Can have extensible plugins/middlewares.
  Higher overhead, but rich functionality.

  In practice: an API Gateway uses a reverse proxy
  internally. What sets it apart is the layer of
  application functionality built on top of it.

When to use

  • Microservices architecture with multiple exposed services: when there are more than two or three services that need to be accessible to external clients, the Gateway eliminates the duplication of infrastructure code in each of them.
  • Centralized authentication: instead of each service implementing and maintaining its own token validation logic, the Gateway validates once and propagates only the necessary context. A change in the authentication strategy (e.g., migrating from JWT to PASETO) happens in one place.
  • Clients with different needs (BFF): mobile app, web SPA, and B2B integrations rarely need exactly the same response format. The BFF allows adapting without contaminating internal services with presentation logic.
  • Traffic control and service protection: rate limiting, throttling, and circuit breaker centralized in the Gateway protect internal services without requiring each one to implement those mechanisms.

When to avoid or be careful

  • Monolith or simple application: adding an API Gateway to a single application is overhead without benefit — the application is already the entry point. The pattern exists to solve the complexity of multiple independent services; without that complexity, it adds latency and an extra component to operate with no gain.
  • Gateway as a god object with business logic: the pattern's most serious risk. Routing based on user state, discount rules, business workflow orchestration — all of that belongs in the services. A Gateway with domain logic becomes a monolith in disguise that couples every service to itself.

Pros and cons

Pros

  • Centralization of cross-cutting concerns: authentication, rate limiting, and logging implemented once, applied consistently across all services.
  • Decoupling from internal topology: clients don't know which services exist internally — internal reorganizations don't break clients.
  • Single point of observability: all requests pass through the Gateway, which can generate metrics, traces, and logs with a correlation ID without depending on each service.
  • Simplification of internal services: each service assumes that whoever reached it has already been authenticated and authorized, and can focus exclusively on its domain logic.
  • Centralized API versioning: multiple API versions can coexist at the Gateway while services evolve internally without breaking contracts.

Cons

  • Single point of failure: if the Gateway goes down, no client can reach any service. High availability of the Gateway isn't optional.
  • Extra latency: every request goes through an extra hop. On latency-critical paths, this overhead (typically 1–5 ms) needs to be accounted for.
  • Risk of logic accumulation: the convenience of centralizing encourages putting business logic in the Gateway over time, creating a deploy bottleneck and a coupling point.
  • Operational complexity: the Gateway needs to be operated, monitored, scaled, and updated like any other critical piece of infrastructure.
  • Scalability bottleneck: all external traffic passes through the Gateway. Scaling the Gateway horizontally needs to be planned before it becomes the system's throughput limiter.

Common pitfalls

1. Business logic in the Gateway

The most common and most serious pitfall. It starts innocently: a routing rule that checks the user's plan, a header that changes based on the account balance, a transformation that applies discount rules. Over time, the Gateway accumulates domain logic that should live in the services.

Rule of thumb: the Gateway should be domain-agnostic. If a routing decision depends on anything beyond the URL, the HTTP method, and technical headers (authentication, content-type), it's a sign that business logic is leaking into the Gateway.

2. Single point of failure without high availability

A Gateway running as a single instance is an existential risk to the system. A hardware failure, a bad deploy, or a traffic spike that exhausts resources takes down every service at once. The Gateway needs multiple instances behind a load balancer, with aggressive health checks and auto-scaling capacity.

3. Rate limiting without adequate granularity

Limiting requests by IP alone penalizes legitimate users who share the same IP address via corporate NAT or mobile carrier CGNAT — hundreds of distinct users behind the same public IP. Limiting by authenticated user (via JWT token or API key) is more precise and fair. Ideally, both mechanisms coexist for different types of protection.

  # Rate limiting by IP: blunt instrument
  # Penalizes groups of legitimate users (NAT, office, campus)

  # Rate limiting by authenticated user: more precise
  # Limits individual behavior, not the shared IP

  # Ideal combination:
  # - By IP: protection against bots and unauthenticated attacks
  # - By user: protection against abuse from authenticated users
  # - By API key: usage control for B2B integrations

4. SSL termination without secure internal communication

Terminating TLS at the Gateway and using plain HTTP internally assumes the internal network is trustworthy. In multi-tenant cloud environments, orchestrated containers, or when compliance requires it (PCI-DSS, HIPAA), the internal network can't be assumed to be secure. In those cases, mTLS (mutual TLS) between Gateway and services guarantees mutual authentication and confidentiality even within the private network.

Related architectures and patterns

Load Balancing and API Gateway operate at distinct layers: the load balancer distributes traffic across instances of the same service (horizontal scale); the API Gateway routes across different services. In production, traffic frequently passes through the Gateway and then through a load balancer before reaching a specific service instance.

The Circuit Breaker is a natural partner of the API Gateway: when a downstream service starts failing, the Gateway can open the circuit breaker and return a fallback response without propagating the instability to clients and without overloading the sick service with requests that will fail anyway.

The choice between REST, GraphQL, and gRPC directly affects the Gateway's responsibilities: a GraphQL API can partially replace the Gateway for composed queries (the client declares what it needs in a single request); internal gRPC can be exposed as external REST by the Gateway via transcoding. The internal protocol doesn't need to match the external protocol.

The CDN frequently operates in front of the API Gateway: the CDN serves static assets at the edge without reaching the Gateway; dynamic requests pass through the CDN (which can help with geographic routing) and reach the Gateway for processing.