API Gateway
Context
Section titled “Context”You have a dozen services, each with its own API and its own network address. A mobile app or a single-page web client needs data from several of them to render a single screen — the order summary alone touches Orders, Inventory, and Customer. If the client talks to each service directly, it has to know every service’s address, make many round trips over a slow mobile network, and implement authentication against each one.
Problem
Section titled “Problem”Exposing internal services directly to external clients couples those clients to your internal structure. The client must discover and call many endpoints, and every time you split or merge a service, every client breaks. Worse, cross-cutting concerns — authentication, TLS termination, rate limiting, request logging — would have to be implemented in every service or in every client. And a chatty client on a high-latency connection pays a round trip per service just to fill one screen. How do you give external clients one stable, efficient, secure place to call, without leaking your internal topology?
Solution
Section titled “Solution”An API Gateway is a single server that sits between external clients and the internal services. It is the one entry point to the system, and it does three jobs:
- Routing — it receives every external request and forwards it to the appropriate service, mapping a public path to an internal address. Clients know only the gateway.
- Aggregation — for a request that needs data from several services, the gateway can fan out, call each one, and compose a single response, so the client makes one call instead of many.
- Offloading cross-cutting concerns — authentication, authorization, TLS termination, rate limiting, request logging, and caching all happen once, at the edge, instead of being duplicated in every service.
flowchart LR Web[Web Client] --> GW Mobile[Mobile Client] --> GW ThirdParty[Third-party API client] --> GW GW[API Gateway<br/>routing - aggregation - auth - rate limit] GW --> O[Order Service] GW --> I[Inventory Service] GW --> C[Customer Service]
Example
Section titled “Example”Gateway behaviour is configuration, not application code: you declare routes and the policies that apply to them, and the gateway engine enforces them. Here is a route configuration that maps public paths to internal services and attaches authentication and rate limiting to each route.
# api-gateway.yaml — routes external paths to internal serviceslisten: ":443"tls: cert: /etc/gateway/tls.crt key: /etc/gateway/tls.key
routes: - path: /api/orders/* upstream: http://order-service methods: [GET, POST] auth: type: jwt issuer: https://auth.example.com rate_limit: requests_per_minute: 120
- path: /api/customers/* upstream: http://customer-service methods: [GET] auth: type: jwt issuer: https://auth.example.com rate_limit: requests_per_minute: 60
# An aggregating route: the gateway fans out and composes one response. - path: /api/order-summary/:id aggregate: - get: http://order-service/orders/:id # as "order" as: order - get: http://customer-service/customers/:customerId as: customer - get: http://inventory-service/stock/:sku as: stock auth: type: jwt issuer: https://auth.example.comResulting context
Section titled “Resulting context”What you gain:
- One simple entry point. Clients know a single host and a stable public API. Internal services can be split, merged, renamed, or moved without breaking any client.
- Cross-cutting concerns in one place. Authentication, TLS, rate limiting, and logging are configured once at the edge instead of being reimplemented in every service.
- Fewer round trips. Aggregation lets a client fill a whole screen with one request, which matters most on high-latency mobile connections.
What it costs you:
- A new component to build and operate. The gateway must itself be highly available and scalable, because it is on the critical path of every request. If it is down, the system is down.
- It can become a bottleneck or a God-object. As more routing rules, transformations, and aggregations accumulate, the gateway grows into a large, shared component that every team must change — and a development bottleneck when one team’s change waits behind another’s.
- Risk of over-generalisation. A single gateway trying to serve a web app, a mobile app, and third-party integrations equally well tends to serve none of them ideally, because their needs differ.
That last cost is the motivation for the next pattern: instead of one gateway for everyone, give each client type its own. See Backends for Frontends.
Related patterns
Section titled “Related patterns”- Backends for Frontends — one tailored gateway per client type, addressing the God-object risk.
- Service Discovery — how the gateway finds live instances of the services it routes to.
- API Composition — the aggregation technique a gateway uses to join data from several services.
| Benefit | Trade-off |
|---|---|
| Clients talk to one place — easy to manage | Single point of failure — if the gateway is down, the whole system is down |
| Cross-cutting concerns (auth, rate limiting) done once | Adds latency — every request has to pass through the gateway |
| Internal services can change without clients noticing | The gateway can become a bottleneck under high load |
| Supports aggregation — one client call can span several services | The gateway itself needs care and feeding, adding operational complexity |
Common mistakes
Section titled “Common mistakes”God gateway — business logic embedded in the gateway instead of just routing and auth. Symptoms:
- The gateway has if/else branches encoding business rules.
- Changing a business rule means changing the gateway.
- The gateway knows more about a domain than the service that owns it.
Gateway coupling — every service can only ship once the gateway is ready. Symptoms:
- The gateway’s release schedule blocks every other service’s release.
- Multiple teams are constantly waiting on the gateway team.
💡 Real-world examples
Netflix: used Zuul (later Zuul 2, a non-blocking gateway built on Netty) as its API gateway, handling billions of requests a day — authentication, routing, and load balancing all happen at the edge. It later added GraphQL Federation behind the gateway to merge hundreds of Domain Graph Services into one unified API graph (Netflix TechBlog: How Netflix Scales its API with GraphQL Federation).
A broader trend: many organizations give each team its own gateway instance (instead of one central gateway everyone has to queue behind), with a platform team owning only shared concerns like the custom domain while each team deploys and manages its own routes independently (AWS Compute Blog: Architecting multiple microservices behind a single domain with Amazon API Gateway).