TL;DR

1. The problem: why does Instagram need an API gateway?

Look at the main architecture diagram. Behind the load balancer there are at least eight application services: Upload, Stories, Like & Comment, Follow/Graph, News Feed, Search, Direct Messaging and User/Auth. In a real company there are hundreds. Now imagine the mobile app talking to each of them directly:

The fix is to put one well-defined front door in front of all the services. Clients talk to i.instagram.com (one host, one API surface); the gateway figures out who you are, whether you're allowed in, whether you're sending too much, and which internal service should handle your request.

🧠 Analogy Think of a large hotel's reception desk. Guests don't wander the corridors looking for the kitchen, the laundry or the spa manager. They go to reception, which checks their room key (authentication), knows which department handles which request (routing), politely refuses the guest who has called 40 times in an hour (rate limiting), translates a request in French for the kitchen staff (transformation), and keeps a log of every request (observability). The departments behind the desk can be reorganised freely β€” guests never notice. That reception desk is the API gateway.

2. Reverse proxy vs load balancer vs API gateway

These three terms overlap and are often used loosely β€” the same piece of software (NGINX, Envoy) can play all three roles. It helps to define them by what they know.

2.1 Forward proxy vs reverse proxy

A proxy is a server that forwards traffic on someone else's behalf.

2.2 The three roles compared

Reverse proxyLoad balancerAPI gateway
Core jobStand in front of servers and forward requests to themSpread traffic across many identical instancesBe the managed entry point to an API made of many services
Knows aboutHosts, paths, headersPool members, their health and loadAPI routes, versions, consumers (users/apps), tokens, quotas, schemas
OSI layerUsually L7 (HTTP)L4 (TCP/UDP) or L7L7 only β€” it must read HTTP methods, paths, headers, bodies
Typical featuresTLS termination, caching, compression, bufferingHealth checks, algorithms (round robin, least connections, consistent hashing), connection drainingAuth, rate limiting, request validation, transformation, aggregation, API keys, developer portal, analytics
Routes toOne or a few backendsMany copies of the same serviceDifferent services depending on the route
ExampleNGINX in front of a Django appAWS NLB, HAProxy, MaglevKong, AWS API Gateway, Apigee, Envoy with auth filters
In Instagram's diagramImplicit β€” both the LB and gateway are reverse proxies"Load Balancer" box"API Gateway" box

A useful mental model: every API gateway is a reverse proxy; every L7 load balancer is a reverse proxy; an API gateway usually contains a load balancer (once it decides "this goes to the Feed service", it still has to pick one of the 2,000 Feed service instances). See Load Balancing for the balancing algorithms.

πŸ“± iOS / Android πŸ’» Web browser Load BalancerL4, spreads load API Gateway fleet TLS termination Token validation Rate limiting Validate + transform Route + balance Logs, metrics, traces Upload Service News Feed Service Like & Comment Follow / Graph Search Service User / Auth path-based routing
The load balancer spreads connections over gateway instances; each gateway instance applies cross-cutting policies and routes each request to the right service.

3. Gateway responsibilities, one by one

Each responsibility below is a filter (also called a plugin, policy or middleware) in a pipeline. A request passes through them in order; any filter can short-circuit the pipeline by returning an error (e.g. 401, 429) without ever touching a backend service.

  request ─▢ [TLS] ─▢ [CORS] ─▢ [AuthN] ─▢ [Rate limit] ─▢ [Validate] ─▢ [Transform] ─▢ [Route/LB] ─▢ service
                                   β”‚            β”‚               β”‚
                                  401          429             400        (short-circuit, backend never sees it)
  response ◀─ [Log/metrics/trace] ◀─ [Cache store] ◀─ [Transform response] ◀─ [Aggregate] ◀──────────── service

3.1 Routing

The gateway maps an incoming request to an upstream service using rules on the host, path, method and sometimes headers:

routes:
  - match: { method: POST,   path: "/v1/media/upload-url" }     -> upload-service
  - match: { method: POST,   path: "/v1/posts" }                -> upload-service
  - match: { method: GET,    path: "/v1/feed" }                 -> feed-service
  - match: { path_prefix: "/v1/posts/{id}/likes" }              -> like-comment-service
  - match: { path_prefix: "/v1/posts/{id}/comments" }           -> like-comment-service
  - match: { path_prefix: "/v1/users/{id}/follow" }             -> graph-service
  - match: { method: GET,    path: "/v1/search" }               -> search-service
  - match: { path_prefix: "/v1/stories" }                       -> stories-service
  - match: { path: "/v1/dm", header: "Upgrade: websocket" }     -> dm-service  (long-lived)
  - match: { path_prefix: "/v1/auth" }                          -> auth-service

After picking the service, the gateway finds healthy instances through service discovery (a registry like Consul, ZooKeeper, Kubernetes endpoints or a DNS name) and picks one using a load-balancing algorithm. Because services can be moved, split or renamed by changing this table, the public API is decoupled from the internal topology β€” the single biggest architectural benefit.

3.2 Authentication (token validation)

Authentication answers "who is this?". After login, the app holds an access token (often a JWT β€” see Authentication & Authorization). On every request the gateway:

  1. Extracts the token from Authorization: Bearer <token>.
  2. Validates it: for a signed JWT, it checks the signature using the auth service's public key (cached locally β€” no network call), the expiry (exp), the issuer and audience. For an opaque token, it looks it up in a fast session store (e.g. Redis) or calls the auth service's introspection endpoint, usually with a short local cache.
  3. Rejects invalid/expired tokens with 401 Unauthorized.
  4. Strips the raw token (optional) and forwards trusted identity headers such as X-User-Id: 1234, X-Device-Id, X-Auth-Scopes to the backend, ideally inside an internal signed envelope so services can trust them.
⚠️ Gateway does AuthN, services do object-level AuthZ The gateway can answer "is this a valid logged-in user?" and coarse questions like "does this third-party app have the comments:write scope?". It cannot answer "may user 1234 see post 987?" β€” that depends on whether the author's account is private, whether 1234 follows them, whether either has blocked the other. Those object-level checks belong in the service that owns the data. Relying only on the gateway leads to IDOR bugs (see Authentication).

3.3 Rate limiting

The gateway is the natural place to enforce limits because it sees every request and already knows the caller's identity. Typical policies: "100 requests/second per user", "60 follow actions per hour per account", "5 login attempts per minute per IP", "5,000 calls/hour per third-party API key". Over the limit β†’ 429 Too Many Requests with a Retry-After header. Counters usually live in a shared Redis cluster so all gateway instances agree. The algorithms (token bucket, sliding window…) are covered in depth in Rate Limiting.

3.4 TLS termination

TLS termination means the gateway (or the LB in front of it) performs the HTTPS handshake and decrypts traffic, so backend services receive plain HTTP or re-encrypted internal mTLS. Benefits:

Options: terminate at the edge (simplest), terminate and re-encrypt to backends (zero-trust networks β€” usually mTLS via a service mesh), or TLS passthrough (an L4 LB forwards encrypted bytes; the gateway terminates). See Networking Basics for how the handshake works.

3.5 Request validation

Reject malformed requests before they consume backend resources: required fields present, types correct, string lengths within limits (caption ≀ 2,200 characters, comment ≀ 300 characters β€” assume these limits), body size ≀ a maximum, content type allowed, no unknown enum values. Usually driven by a schema (OpenAPI/JSON Schema, or protobuf definitions). Failures return 400 Bad Request or 413 Payload Too Large. This also blunts some attacks (giant payloads, deeply nested JSON bombs).

3.6 Transformation (request and response)

The gateway can rewrite traffic so clients and services evolve independently:

3.7 Response aggregation / composition

One client request fans out to several services in parallel, and the gateway merges the results. This turns N round trips over a slow mobile network into one, and the fan-out happens over the fast data-center network (sub-millisecond hops).

 App ── GET /v1/profile/natgeo ──▢ Gateway / BFF
                                     β”œβ”€β”€β–Ά User service      (name, bio, avatar)         12 ms
                                     β”œβ”€β”€β–Ά Graph service     (followers, following)       8 ms
                                     β”œβ”€β”€β–Ά Post service      (first 12 grid thumbnails)  20 ms
                                     └──▢ Stories service   (highlights, active story)  10 ms
                                     (all in parallel β†’ total β‰ˆ max = 20 ms + merge)
 App ◀── one JSON document β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Worked example: on a 4G connection with an 80 ms round-trip time, four sequential client calls cost β‰ˆ 4 Γ— 80 = 320 ms of network alone. With aggregation: one 80 ms round trip + ~20 ms of parallel internal calls β‰ˆ 100 ms. That's a 3Γ— faster profile screen.

Aggregation needs care: set a timeout per dependency and decide what's optional. If the Stories service is slow, return the profile without highlights (a partial response) rather than failing the whole page β€” see Resilience patterns. Heavy aggregation logic is better placed in a BFF or GraphQL layer (below) than in a generic gateway.

3.8 Caching

A gateway can cache responses for public, non-personalised GET requests: e.g. a hashtag's top posts for 30 seconds, public profile metadata for 10 seconds, config/feature-flag payloads for a minute. Cache keys must include anything that changes the response (path, query, Accept-Language, API version, and the user ID if the response is personalised β€” in which case shared caching rarely pays off). Personalised data like the feed is cached deeper, in the Redis feed cache owned by the Feed service. Media bytes are cached by the CDN, not the gateway.

3.9 CORS

CORS (Cross-Origin Resource Sharing) is the browser mechanism that decides whether JavaScript on one origin (say https://www.instagram.com) may call an API on another origin (https://i.instagram.com). For "non-simple" requests (e.g. with an Authorization header or JSON body), the browser first sends an OPTIONS preflight request. The gateway answers it centrally:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://www.instagram.com
Access-Control-Allow-Methods: GET, POST, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type, Idempotency-Key
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 600

Handling CORS at the gateway means one consistent allow-list, and preflights never reach backend services. Never reflect arbitrary Origin values together with Allow-Credentials: true β€” that effectively disables the browser's protection. CORS only matters for browsers; native mobile apps are not subject to it.

3.10 Logging, metrics and tracing

Because every request passes through it, the gateway is the best place to observe the whole API (see Observability):

3.11 Canary releases and A/B routing

Since the gateway decides where requests go, it can split traffic deliberately:

route: GET /v1/feed
  split:
    - weight: 99   upstream: feed-service-stable
    - weight: 1    upstream: feed-service-canary      # sticky on hash(user_id)
  mirror: feed-service-shadow (10% of requests, responses ignored)

3.12 Other responsibilities you'll hear about

πŸ”Œ Circuit breaking & retries

Stop sending traffic to an upstream that's failing; retry idempotent GETs on another instance. See Resilience patterns.

🧯 Load shedding

When overloaded, drop low-priority traffic (prefetch, analytics) first so core actions (posting, DMs) survive.

πŸ”‘ API keys & developer portal

For third-party apps: issue keys, enforce quotas and plans, show usage analytics.

πŸ›‘οΈ WAF / IP filtering

Block known-bad IPs, SQL-injection-like patterns, bots. Often done even earlier, at the CDN/edge. See Security.

4. The Backend-for-Frontend (BFF) pattern

A single generic gateway serving every client eventually becomes awkward. The iOS app, the Android app and the web app need different shapes of data:

The Backend-for-Frontend pattern (coined at SoundCloud) gives each client experience its own thin backend layer, usually owned by the team that builds that client:

πŸ“± iOS app πŸ“± Android app πŸ’» Web app Edge gatewayTLS, AuthN,rate limits Mobile BFFcompact, aggregated Web BFFSSR data, richer User service Feed service Post service Graph service Stories service
A shared edge gateway does generic policies; per-experience BFFs shape and aggregate data for their client; domain services stay client-agnostic.
Single general-purpose gatewayBFF per client type
Payload shapeOne-size-fits-all; clients over- or under-fetchExactly what each screen needs
OwnershipCentral platform team becomes a bottleneck for every client changeClient teams own their BFF and ship independently
DuplicationNoneSome logic duplicated across BFFs (mitigate with shared libraries)
Operational costOne fleetSeveral fleets to deploy and monitor
Old-client supportVersion shims pile up in one placeMobile BFF holds mobile-specific compatibility code
πŸ’‘ Rule of thumb Put generic, security-critical policies (TLS, AuthN, rate limits, WAF) in the shared edge gateway. Put client-specific composition and shaping in BFFs. Put business rules (who can see what, how a like is counted) in domain services. Never put business rules in the gateway.

BFF granularity: "one per platform" (iOS, Android, web) is common; many companies use one mobile BFF for both iOS and Android because their needs are nearly identical. A GraphQL layer (Section 7) can replace multiple BFFs by letting each client ask for exactly the fields it wants.

5. North-south vs east-west traffic: gateway vs service mesh

Data-center diagrams are usually drawn with the internet at the top and servers below, so:

East-west traffic needs many of the same features (load balancing, retries, timeouts, mTLS, metrics, tracing) but routing it all through a central gateway would add a hop and create a giant bottleneck. Instead, a service mesh (Istio, Linkerd, Consul Connect) puts a small proxy β€” a sidecar, usually Envoy β€” next to every service instance. Services talk to localhost; the sidecar does discovery, load balancing, mTLS encryption and identity, retries, circuit breaking and telemetry. A central control plane pushes configuration to all sidecars.

πŸ“± Clients (internet) API Gateway Feed service Graph service Post service Envoy sidecar Envoy sidecar Envoy sidecar north-south (gateway) east-west (service mesh, mTLS between sidecars)
The gateway guards the front door; sidecar proxies in a service mesh manage traffic between services.
API gatewayService mesh
TrafficNorth-south (external clients)East-west (service-to-service)
DeploymentCentralised fleet at the edgeDistributed sidecar per instance + control plane
Main concernsEnd-user auth, API keys, rate limits per user, CORS, API versioning, aggregationService identity (mTLS), retries, timeouts, circuit breaking, internal load balancing, telemetry
Trust modelCaller is untrusted (the internet)Caller is another service β€” still verified (zero trust)
ExamplesKong, AWS API Gateway, Apigee, Envoy GatewayIstio, Linkerd, Consul Connect

They are complementary, and often built on the same proxy (Envoy) β€” the gateway is sometimes called the mesh's "ingress gateway".

6. Keeping the gateway from becoming a SPOF or bottleneck

Every single API request passes through the gateway, so if it's down, Instagram is down; if it's slow, everything is slow. Design principles:

6.1 Stateless and horizontally scaled

6.2 Fail sensibly when dependencies fail

6.3 Latency overhead budget

The gateway adds an extra network hop plus processing. Typical costs:

StepTypical costHow to keep it low
Extra hop inside the data center0.2–0.5 msKeep-alive connection pools to upstreams; HTTP/2 multiplexing
TLS handshake (new connection)1–2 RTTs (client-side)TLS 1.3, session resumption, long-lived connections from the app
JWT signature check~0.05–0.2 ms (asymmetric verify)Cache keys; prefer fast algorithms (Ed25519/ECDSA)
Rate-limit check in Redis~0.3–1 msPipelining, Lua scripts, local token buckets with periodic sync
Logging/metrics~0Asynchronous, batched, sampled tracing

Target: total gateway overhead of ~1–5 ms at p99 on a request whose end-to-end budget is ~200 ms. Avoid putting slow work (synchronous calls to many services, heavy transformations, large body buffering) in the generic gateway path. Media uploads should bypass the gateway entirely by using pre-signed URLs straight to object storage β€” the gateway only hands out the URL.

🚫 Anti-pattern: the "smart gateway" monolith Teams sometimes keep adding business logic to the gateway ("just compute the badge count here"). Soon the gateway is a distributed monolith that every team must change and deploy, with no clear owner. Keep the gateway dumb about the business and smart about traffic.

7. GraphQL gateways

GraphQL (created at Facebook, open-sourced in 2015) lets the client send a query describing exactly the fields it wants; the server returns exactly that shape. A GraphQL gateway exposes one unified schema and resolves each field by calling the appropriate backend service.

query ProfileScreen {
  user(username: "natgeo") {
    fullName
    avatarUrl(size: 150)
    followerCount
    isFollowedByViewer
    posts(first: 12) { edges { node { id thumbnailUrl likeCount } } }
    storyHighlights { id title coverUrl }
  }
}

The gateway's resolvers map user β†’ User service, followerCount/isFollowedByViewer β†’ Graph service, posts β†’ Post service, storyHighlights β†’ Stories service, run them in parallel, and assemble one response. This gives aggregation and per-client shaping without writing a separate BFF endpoint for every screen.

Comparison with REST and gRPC is in API Design.

8. How Instagram uses its API gateway

πŸ“Έ In Instagram Instagram began as a Django (Python) monolith behind NGINX/load balancers. As it grew inside Meta, it adopted Meta's edge infrastructure β€” public engineering talks describe Meta's Proxygen-based edge and data-center load balancers terminating TLS and routing to app servers, and Meta is publicly known as the creator of GraphQL, which Instagram clients use for many surfaces. In this course's diagram, those layers are drawn simply as Load Balancer β†’ API Gateway, where the gateway owns AuthN/Z, rate limiting, routing and TLS.

8.1 One request, step by step: liking a post

Let's follow POST /v1/posts/987/likes from the app:

  1. DNS + connection. The app already has a warm HTTP/2 (or HTTP/3/QUIC) connection to i.instagram.com, resolved via DNS to the nearest edge. No new handshake needed.
  2. Load balancer. The L4 load balancer hashes the connection to one healthy gateway instance in the region.
  3. TLS termination. The gateway decrypts the request. It now sees method, path, headers and body.
  4. Assign a request ID and start a trace. X-Request-Id: 7f3c… and a traceparent header are added; a timer starts.
  5. Basic hygiene. Body size within limits? Content-Type allowed? Client version still supported (very old app builds get 426 Upgrade Required / a force-upgrade message)?
  6. Authenticate. Parse Authorization: Bearer …, verify signature and expiry with the cached public key β†’ user ID 1234, device ID d-55. Invalid β†’ 401; the app uses its refresh token to get a new access token.
  7. Rate limit. Check buckets in Redis: the general per-user request limit and the action-specific "likes per hour" limit used against spam bots. Over the limit β†’ 429 with Retry-After.
  8. Validate. Is 987 a well-formed post ID? Is an Idempotency-Key present for this write?
  9. Route. The routing table says /v1/posts/{id}/likes β†’ Like & Comment Service. Service discovery returns healthy instances; the gateway picks one (least-outstanding-requests) and forwards over a pooled connection, adding X-User-Id: 1234.
  10. Service does the real work. The Like service checks object-level permission (is the post visible to 1234? is 1234 blocked by the author?), writes the like, increments the counter in Redis, and publishes a like.created event to Kafka (consumed by the Notification service to push "user liked your photo").
  11. Response path. The gateway receives 200 {"liked": true, "like_count": 10431}, strips internal headers, adds rate-limit headers, compresses, and returns it.
  12. Observe. The gateway asynchronously emits an access-log line and metrics (route=likes.create, status=200, latency=18 ms) and closes its trace span.
πŸ“± AppPOST like
β†’
Load BalancerL4
β†’
API GatewayTLS Β· AuthN Β· 429 Β· route
β†’X-User-Id
Like & CommentAuthZ + write
β†’event
Kafkalike.created

8.2 Rough numbers

Assume 500M daily active users, each making ~200 API calls/day (feed pages, stories, likes, prefetch, telemetry):

500M Γ— 200 = 100 billion API requests/day
100e9 / 86,400 s β‰ˆ 1.16M requests/s average; assume peak β‰ˆ 2–3Γ— β†’ ~3M req/s
If one gateway instance comfortably handles ~20k req/s at p99 < 5 ms:
   3M / 20k = 150 instances at peak, Γ—1.5 headroom for zone failure β‰ˆ 225 instances worldwide
Gateway overhead 2 ms Γ— 100B requests/day β‰ˆ 200M seconds of added latency per day β€”
   which is why every millisecond in the gateway path is engineered away.

Note what does not go through the gateway: photo/video bytes (served by the CDN and uploaded directly to object storage via pre-signed URLs). This keeps the gateway handling small JSON requests only.

9. Trade-offs & common pitfalls

βœ… Benefits

  • One stable public API; internal services can be split, moved or rewritten freely
  • Cross-cutting concerns implemented once, consistently and securely
  • Smaller attack surface β€” only the gateway is internet-facing
  • Fewer client round trips via aggregation
  • Central visibility: per-route metrics, logs, traces
  • Safe deploys: canaries, A/B splits, shadow traffic

⚠️ Costs

  • Extra hop and a few ms of latency on every request
  • Potential single point of failure and bottleneck if not scaled and isolated
  • A misconfiguration can take down the whole API at once
  • Risk of becoming a "smart" monolith that every team must change
  • Another critical system to operate, upgrade and secure

Common pitfalls

10. Real-world technologies

ToolWhat it isWhen to use it
NGINX / OpenRestyHigh-performance reverse proxy and web server; OpenResty adds Lua scriptingClassic reverse proxy, TLS termination, simple routing and rate limiting; base for many gateways
EnvoyModern L4/L7 proxy (from Lyft) with dynamic config APIs, gRPC, HTTP/2/3, rich observabilityService-mesh sidecars and edge gateways (Envoy Gateway, Istio ingress); gRPC-JSON transcoding
KongAPI gateway built on NGINX/OpenResty with a plugin ecosystem (auth, rate limiting, transformations)Self-hosted, plugin-driven API management, API keys and developer portals
AWS API GatewayFully managed gateway: REST, HTTP and WebSocket APIs, Lambda integration, usage plansServerless and AWS-native APIs where you don't want to run gateway servers
Google ApigeeEnterprise API management: policies, monetisation, analytics, developer portalExposing APIs to partners/third parties with quotas, billing and analytics
Azure API Management, Tyk, KrakenD, TraefikOther managed/open-source gateways; KrakenD focuses on aggregation, Traefik on KubernetesCloud-specific or Kubernetes-native setups
Apollo Router / GraphQL MeshGraphQL federation gatewaysUnifying many team-owned GraphQL subgraphs
Istio / LinkerdService meshes (control plane + sidecar proxies)East-west traffic: mTLS, retries, traffic splitting between services
Proxygen (Meta)Meta's open-source C++ HTTP framework used in its edge proxiesExample of a hyperscaler building its own proxy layer

11. Interview corner

What's the difference between a load balancer, a reverse proxy and an API gateway?

A reverse proxy is any server that accepts client requests on behalf of hidden backends (TLS termination, caching, buffering). A load balancer is a reverse proxy (or L4 device) whose job is spreading traffic across many identical instances of one service using health checks and algorithms. An API gateway is an L7 reverse proxy that understands the API: it routes different paths to different services, authenticates users, enforces per-consumer rate limits and quotas, validates and transforms requests, and aggregates responses. In practice an API gateway also load-balances across instances of each upstream, and the same software (Envoy, NGINX) can fill all three roles.

Where do you do authentication and where do you do authorization in a microservice architecture?

Authentication (verifying the token and establishing the user ID) at the gateway, once, so services don't each re-implement it; the gateway forwards a trusted identity (header within mTLS or a signed internal token). Coarse authorization (scopes for third-party apps, "is this an admin endpoint") can also be at the gateway. Fine-grained, object-level authorization β€” can user A see post B, given private accounts, blocks and close-friends lists β€” must be in the service that owns the data, because only it has that data. Defense in depth: services should still reject requests that didn't come through the gateway.

Isn't the API gateway a single point of failure? How do you avoid that?

It's a single logical point, not a single machine. Make it stateless (identity in tokens, counters in Redis), run many instances across availability zones and regions behind redundant L4 load balancers/anycast, health-check and auto-scale them. Avoid hard runtime dependencies: verify JWTs locally with cached keys, fail open with local limits if the rate-limit store is down. Isolate traffic classes with separate pools. The biggest real risk is a bad config push, so stage config rollouts region by region with automated rollback.

How would you design the API layer so mobile and web clients both get good performance?

A shared edge gateway for generic concerns (TLS, auth, rate limiting) plus a Backend-for-Frontend per experience: a mobile BFF returning compact, screen-oriented aggregated payloads with smaller image renditions and holding compatibility shims for old app versions, and a web BFF for richer/SSR data. Alternatively a GraphQL layer with persisted queries, letting each client request exactly the fields it needs. Domain services remain client-agnostic.

How would you roll out a new version of the Feed service safely?

Use the gateway's weighted routing: deploy v2 beside v1, optionally mirror shadow traffic first to compare responses and latency, then route 1% of users (sticky by hash(user_id)) to v2, watch error rate, p99 latency and business metrics (feed engagement), and ramp 5% β†’ 25% β†’ 100% with automated rollback if SLOs degrade. Because routing is config, rollback takes seconds.

What's the difference between an API gateway and a service mesh? Do you need both?

The gateway manages north-south traffic from untrusted clients: user auth, API keys, per-user rate limits, CORS, versioning, aggregation. The mesh manages east-west traffic between services via sidecar proxies: mTLS service identity, retries, timeouts, circuit breaking, load balancing and telemetry. Large systems with many services typically use both β€” often both built on Envoy. Small systems can skip the mesh and use client libraries.

A client uploads a 200 MB Reel. Should that go through the gateway?

No. The app calls the gateway for a small request (POST /v1/media/upload-url), which is authenticated and rate-limited; the Upload service returns a short-lived pre-signed URL; the app uploads bytes directly to object storage (often resumable/chunked). This keeps gateway instances from tying up memory and connections for minutes and avoids paying double bandwidth. See Media Upload.

12. Check your understanding

Which of these belong in the gateway: (a) JWT signature validation, (b) "is user 42 blocked by the author of post 9?", (c) per-user rate limits, (d) computing a post's like count, (e) CORS preflight responses?

(a), (c) and (e). They're generic cross-cutting concerns that need no domain data. (b) is object-level authorization requiring the block list β€” it belongs in the service that owns posts/graph. (d) is business logic owned by the Like & Comment service.

The profile screen needs 4 backend calls. Mobile RTT is 100 ms; each internal call takes ~15 ms. Estimate the time with and without gateway aggregation (ignore server think time beyond that).

Without aggregation (sequential client calls): 4 Γ— (100 + 15) β‰ˆ 460 ms. Even if the client parallelises them, it's β‰ˆ 115 ms but uses 4 requests and more radio/battery. With aggregation: 1 Γ— 100 ms + ~15 ms (parallel internal calls) + ~1–2 ms gateway overhead β‰ˆ 117 ms, using a single request. The big win is avoiding sequential mobile round trips.

An attacker sends X-User-Id: 1 in their request headers. What should happen?

The gateway must strip any incoming identity headers and set them only from the validated token. Services should accept identity headers only over authenticated internal channels (mTLS from the gateway/mesh). Otherwise an attacker could impersonate any user β€” a classic header-spoofing vulnerability.

Why must a gateway be stateless, and where does the "state" go instead?

So any instance can handle any request, instances can be added or killed freely, and failures don't lose user data. State moves to: the token itself (JWT claims), shared stores (Redis for rate-limit counters, session stores), and config distributed from a control plane.

Is traffic from the Feed service to the Graph service north-south or east-west? What component typically manages it?

East-west (service-to-service inside the data center). A service mesh's sidecar proxies (or RPC client libraries) manage it β€” not the public API gateway, which would add an unnecessary hop and bottleneck.

You route 2% of users to a canary using a random number per request rather than hash(user_id). What goes wrong?

Each user's requests bounce randomly between versions, so a single session mixes v1 and v2 behaviour (inconsistent UI, cache confusion), and you can't attribute user-level metrics to a version. Hash-based sticky bucketing keeps each user on one version and makes rollouts and experiments measurable.

Gateway overhead rises from 2 ms to 12 ms at p99 after enabling a new plugin. Name two likely causes and fixes.

(1) A synchronous network call per request (e.g. token introspection or a remote policy check) β€” fix with local JWT verification or a short-TTL local cache. (2) Buffering/transforming full request bodies β€” fix by streaming, or moving heavy transformation to a BFF. Others: rate-limit checks without pipelining (use Lua scripts / local buckets), synchronous logging (make it async).

🎯 Key takeaways

Related lessons