API Gateway Pattern
Concept
Once a system is decomposed into many microservices, a naive client (a mobile app, a web SPA, a third-party integration) would have to know about, discover, and call each service directly. That leaks the internal service topology to every client, forces each client to handle cross-cutting concerns (auth, retries, TLS) itself, and makes the network chatty — a single screen might need calls to a dozen services.
The API gateway pattern (popularized by Chris Richardson's microservices.io) inserts a single entry point between external clients and the internal services. It is the one public front door. Its responsibilities fall into two buckets:
- Routing / composition — accept a client request and route it to the right internal service, or fan out to several services and aggregate their responses into one payload (API composition), so a client makes one call instead of ten.
- Cross-cutting concerns, centralized — authentication and authorization, TLS termination, rate limiting and throttling, request/response logging and metrics, caching, request validation, and protocol translation (e.g. external REST/GraphQL → internal gRPC). Doing these once at the edge means each internal service doesn't re-implement them.
The gateway also decouples the client from the internal topology: services can be split, merged, renamed, or relocated behind the gateway without breaking clients, because the gateway presents a stable external contract.
A common refinement is Backend for Frontend (BFF) — instead of one gateway serving all client types, you run a separate gateway per client class (one for mobile, one for web, one for partners), each tailored to that client's needs. See BFF for that specialization; the API gateway is the general pattern, BFF is the "one gateway per experience" variant.
Tradeoffs
| Concern | With API gateway | Without (direct client-to-service) |
|---|---|---|
| Client complexity | Low — one endpoint, one auth handshake, one call per screen | High — clients discover and call each service, handle N auth/retry/TLS setups |
| Cross-cutting concerns | Centralized once at the edge | Duplicated in every service or every client |
| Coupling to topology | Loose — internal changes hidden behind a stable contract | Tight — any service change can break clients |
| Failure domain | Gateway is a critical shared path; must be HA | No single shared choke point |
| Latency | One extra network hop; composition can hide fan-out latency | Direct calls, but chatty for multi-service screens |
The dominant risk is that the gateway becomes a single point of failure and a scaling/deployment bottleneck — every request flows through it, so it must be horizontally scaled and highly available, and if every team has to modify the gateway to ship a feature, it becomes a coordination chokepoint (the "distributed monolith" smell). The discipline is to keep the gateway thin: routing, auth, and cross-cutting concerns — not business logic. Business logic belongs in the services behind it.
When to use / when not to
- Use whenever multiple external client types consume a microservices backend and you want to hide internal topology and centralize auth, rate limiting, and TLS.
- Use when client-side call chatter is a real problem — a gateway doing API composition turns a dozen client round-trips into one.
- Strongly consider the BFF variant when different clients (mobile vs. web vs. partner API) need materially different payloads and aggregation — a single one-size-fits-all gateway ends up bloated with client-specific logic.
- Don't add one for a single monolith or a single service — there's nothing to route, compose, or hide, so it's pure overhead and an extra hop.
- Don't let it accumulate business logic — the moment domain rules live in the gateway, you've built a new monolith that every team must coordinate through.
Common pitfall
Letting the gateway drift from "thin edge concern handler" into "place where business logic lives." It starts innocently — one endpoint needs a small transformation, then a special-case validation, then some orchestration — and soon the gateway encodes domain rules that belong in services. Now every feature requires a gateway change, teams serialize behind it, and a gateway deploy can break unrelated features: you've recreated the monolith you decomposed to escape, plus an extra network hop. The related pitfall is not treating the gateway as a first-class HA component — because everything flows through it, an under-provisioned or single-instance gateway is a system-wide single point of failure.
Engineering Lens
The API gateway is a good test of whether an architect understands the difference between an edge concern and a domain concern — the reviewable question is always "what lives in the gateway, and why isn't it business logic?" A strong design keeps the gateway to routing, authN/authZ, rate limiting, and composition, and can articulate that separation crisply, because that boundary is what keeps team autonomy intact (teams ship services without coordinating on a shared gateway). In Fintech and BigTech contexts the gateway is also the natural security and governance chokepoint — the single place to enforce authentication, mTLS to internal services, request validation, and audit logging on every external call — which is a defensible answer to "where do you enforce access control across all your APIs." The failure mode to name in review is the gateway becoming a distributed-monolith bottleneck; the mitigation (thin gateway + per-client BFFs) is the Principal-level nuance.