API Gateway & Backend-for-Frontend
A thin, horizontally scaled gateway owns north-south cross-cutting concerns, BFFs shape payloads per client type, the mesh owns east-west, and business logic stays in services.
Where the cross-cutting concerns live
As a monolith splits into dozens of microservices, a hard question appears: where do the cross-cutting concerns live? Every client call needs authentication, TLS, rate limiting, routing, and observability. You do not want thirty services reimplementing all of that, and you do not want each client talking directly to thirty services. The API gateway is the single north-south entry point that owns those concerns so the services behind it stay thin.
A gateway centralizes: TLS termination, authentication and authorization, rate limiting and quotas, routing, request/response transformation, response aggregation, API versioning and canary routing, and observability. Concrete implementations: Kong, AWS API Gateway, Envoy-based gateways, Apigee, or a Netflix Zuul-style edge service.
Draw the boundary carefully. The gateway handles north-south traffic (client to system). Service-to-service east-west traffic is the job of a service mesh (Istio, Linkerd, Envoy sidecars), which handles mTLS, retries, and load balancing between services. Routing internal calls through the public gateway is a common design error. Business logic belongs inside services, not in either the gateway or the mesh.
Sort each concern into the layer that should own it.
Backend-for-frontend (BFF)
One generic API rarely fits every client. A mobile app on a slow network wants a small, denormalized
payload in one round trip; a web SPA wants richer data; a partner API needs stable, versioned
contracts. A single endpoint serving all three leads to over-fetching (mobile downloads fields it
never renders) or under-fetching (five calls to build one screen). A BFF is a thin gateway
per client type: bff-mobile, bff-web, bff-partner. Each aggregates and shapes exactly
what its client needs and is owned by that client's team, so a mobile change does not ripple through
the web contract. GraphQL is one way to give clients field-level selection and reduce the need for
many hand-written BFFs, at the cost of its own query-cost and caching complexity.
Stage 1 of 4: Three client types need different payloads: mobile wants a small denormalized response in one round trip, the web SPA wants richer data, and the partner needs stable versioned contracts. One generic endpoint over-fetches or under-fetches.
The two big risks
First, the gateway is a single point of failure and a latency tax: every request pays one extra hop, and if it is down the whole product is down. So it must be horizontally scaled, stateless, health-checked, and kept fast (offload heavy work, cache authz decisions and hot responses). Second, and worse, the gateway can rot into a god-object: teams keep adding "just one more" piece of business logic until the edge holds orchestration and domain rules that belong in services. Then every service change requires a gateway change, deploys serialize on one component, and you have rebuilt the distributed monolith you split up to avoid.
Interview nuance: the strongest answers name what does not belong at the gateway (domain business rules, per-feature orchestration, data ownership) as crisply as what does. Pre-empt the god-object probe by stating a rule: the gateway does cross-cutting concerns and routing only, business logic lives in the owning service.
Recap: put auth, TLS, rate limiting, routing, and observability at a horizontally scaled gateway for north-south traffic, use a BFF per client type to avoid over/under-fetching, leave service-to-service concerns to the mesh, and hold the line against business logic creeping into the edge.
Apply
Your turn
The task this lesson builds to.
Design an API gateway layer for a microservices product with web, mobile, and partner API clients.
Think about
- What belongs at the gateway vs inside services vs the mesh?
- When is a BFF the right pattern?
- How do you keep the gateway from becoming a god-object?
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Design the API gateway and BFF layer for Netflix-scale streaming, where a single home-screen load fans out to dozens of microservices (recommendations, artwork, continue-watching, billing status) and the same backend must serve TVs, phones, browsers, and game consoles with wildly different capabilities. Lead with the request topology.
Think about
- Why does one generic API fail across TVs, phones, and consoles?
- At a dozens-of-services fan-out, what must happen when one non-critical service is slow?
- Which fields fail differently from cosmetic ones?