System DesignIntermediate 12 min Lesson 34 of 42

API Gateway

A single entry point that routes, authenticates, rate limits and shapes traffic for everything behind it.

System Design · Lesson 34 of 42
0/42 done(0%)

What is it? #

An API gateway is a single entry point in front of several backend services. It routes each request to the right service and handles the concerns common to all of them.

Those shared concerns are the reason it exists: authentication, rate limiting, TLS termination, request logging, CORS and response shaping. Implementing each of those in every service is duplicated work and inconsistent in practice.

It also decouples clients from your internal layout. Clients call one address; you can split, rename or move services behind it without touching them.

The risk is concentration. A gateway that accumulates business logic becomes a bottleneck for both traffic and development.

Think of it like this #

A building reception for a complex with many offices. Visitors check in once, are directed to the right floor, and the security and visitor log are handled in one place.

If reception started making decisions on behalf of each office, every office change would require retraining reception.

Simple example #

Mobile and web clients call one domain. Behind it, five services handle catalogue, orders, payments, users and search. The gateway authenticates once and routes by path.

Code #

TEXT
                        ┌────────────────────────┐
  mobile ──┐            │      API GATEWAY       │
  web    ──┼───────────▶│  TLS, auth, rate limit │
  partner──┘            │  routing, logging      │
                        └───────┬────────────────┘
          ┌───────────┬─────────┼─────────┬────────────┐
          ▼           ▼         ▼         ▼            ▼
      catalogue    orders   payments    users       search
YAML
# Routing and shared policy, expressed as configuration
routes:
  - path: /api/v1/products/*
    service: catalogue
    auth: optional                 # public browsing
    rate_limit: { requests: 200, per: minute, key: ip }
    cache: { ttl: 60 }

  - path: /api/v1/orders/*
    service: orders
    auth: required
    rate_limit: { requests: 60, per: minute, key: user }
    timeout: 5s

  - path: /api/v1/payments/*
    service: payments
    auth: required
    scopes: [payments:write]
    rate_limit: { requests: 10, per: minute, key: user }
    timeout: 15s
    retries: 0                     # never retry a charge automatically
PYTHON
# What the gateway does per request, in order
def handle(request):
    request_id = request.headers.get("X-Request-ID") or new_id()

    route = match_route(request.path)                  # 1. routing
    if route is None:
        return response(404)

    if route.auth == "required":                       # 2. authentication
        claims = verify_token(request.headers.get("Authorization"))
        if claims is None:
            return response(401)
        if not has_scopes(claims, route.scopes):       # 3. coarse authorisation
            return response(403)
        request.headers["X-User-Id"] = claims["sub"]   # pass identity inward

    if not rate_limiter.allow(route, request):         # 4. rate limiting
        return response(429, headers={"Retry-After": "30"})

    request.headers["X-Request-ID"] = request_id       # 5. tracing
    upstream = forward(route.service, request, timeout=route.timeout)
    log_access(request, upstream, request_id)          # 6. logging
    return upstream

How it works #

Routing is matched by path, and often also by host or header. The client knows one address; the mapping to services is configuration.

Authentication happens once at the edge. The gateway verifies the token and passes a verified identity header inward, so services do not each implement token validation.

That inward trust must be protected. Services should accept identity headers only from the gateway — on a private network, or by verifying a signature — otherwise anyone who can reach a service directly can impersonate any user.

Authorisation at the gateway is coarse: does this token carry the scope for this route. Fine-grained rules, such as whether this user owns this specific order, belong in the service that holds the data.

Rate limits differ per route, which is exactly the point. Public browsing can be generous; payment endpoints should not be.

retries: 0 on payments is deliberate. An automatic retry of a charge risks a double payment, so retries there must be explicit and idempotency-keyed.

The request ID is generated or propagated, then passed to every service, which is what makes a distributed trace possible.

Real-world use #

Kong, Envoy, Traefik, NGINX and managed cloud gateways all fill this role. In Kubernetes, an ingress controller or service mesh gateway does the same job.

The backend-for-frontend pattern extends the idea: a separate gateway per client type, so the mobile API can aggregate several service calls into one response suited to a phone, without complicating the web API.

That aggregation is genuinely useful on mobile networks, where each round trip is expensive. It is also where gateways start to accumulate logic, which is the main thing to guard against.

A gateway is on the critical path for every request, so it needs the same redundancy and monitoring as any other tier — usually more.

For a single service, a gateway is unnecessary. A reverse proxy already provides TLS, routing and basic limits, which is all you need until there are several services to unify.

Common mistakes #

  • Putting business logic in the gateway, turning it into a deployment bottleneck.
  • Services trusting identity headers from any source, allowing impersonation.
  • Running a single gateway instance, creating a system-wide single point of failure.
  • Automatically retrying non-idempotent requests such as payments.
  • Adding a gateway for a single service, where a reverse proxy would do.

Practice #

Write the routing table for four services with different authentication requirements, rate limits and timeouts. Mark which routes are public, which need scopes, and which must never be retried. Then state how the services will verify that a request genuinely came through the gateway.

Quick quiz

  1. 1. What is the main purpose of an API gateway?

  2. 2. Which authorisation belongs in the service rather than the gateway?

  3. 3. Why must services validate where identity headers come from?

  4. 4. Why disable automatic retries on payment routes?

  5. 5. When is a gateway unnecessary?

Summary

  • A gateway is one entry point that routes and handles shared concerns.
  • Authenticate once at the edge and pass verified identity inward.
  • Keep fine-grained authorisation in the services that own the data.
  • Per-route limits, timeouts and retry policies are the main configuration.
  • Keep business logic out of it, and make it redundant.