API Gateway Patterns: Routing, Aggregation, and Backend for Frontend
Once a system is decomposed into a dozen or more independently deployable services, the question of how clients actually reach those services stops being an implementation detail and becomes an architectural decision in its own right. Get it wrong and every client โ mobile app, web app, partner integration โ ends up encoding knowledge about your internal topology that should never have left the backend.
Letting clients call microservices directly doesn't scale past a handful of services. Every client now needs to know where each service lives, handle a different auth flow per service if policies drift, absorb N round trips (often over a slow mobile network) to render a single screen, and break whenever a service is split, merged, or renamed. An API gateway exists to absorb that complexity at the edge so the internal service topology can evolve without every client redeploying in lockstep.
What an API gateway actually centralizes
At its core, a gateway is a single entry point that terminates external traffic and forwards it to the right internal service. But the routing itself is rarely the interesting part โ the value is in the cross-cutting concerns it pulls out of every individual service:
- Routing โ mapping an external path, host, or header to the correct backend service, including versioned or canary routes.
- Authentication and authorization enforcement โ validating JWTs, checking mTLS client certs, or calling out to an identity provider once, at the edge, instead of every service reimplementing token validation.
- Rate limiting and throttling โ enforcing per-client or per-API-key quotas before a burst of traffic ever reaches a backend service.
- TLS termination โ handling the public-facing certificate so internal service-to-service traffic can run on a simpler, often mutual-TLS-secured, internal network.
- Request and response logging โ capturing a consistent access log and injecting correlation/trace IDs so a single request can be followed across every downstream hop.
The unifying idea is that none of this is domain logic. It's infrastructure concern that, left unmanaged, gets copy-pasted into every service's codebase with subtle inconsistencies โ one service validates tokens slightly differently, another forgets to rate-limit, a third logs in a different format. Centralizing it at the edge means service teams write business logic and trust that everything reaching them has already been authenticated, rate-limited, and logged consistently.
A gateway is one front door with a receptionist who checks ID โ the alternative is a building where every tenant runs their own security desk, and no two of them agree on what counts as valid identification.
Simple routing vs request aggregation
The simplest gateway pattern is pure reverse-proxy routing: request comes in for /orders/*, it goes to the orders service; /catalog/* goes to the catalog service. The gateway's routing table is essentially declarative โ path or host in, upstream out, maybe with header injection or path rewriting. No business logic, easy to reason about, easy to test.
Aggregation is a different animal. Instead of forwarding a single request to a single service, the gateway fans out to multiple backend services in parallel or in sequence and composes a single response โ for example, a "product detail" endpoint that pulls pricing, inventory, reviews, and recommendations from four separate services and returns one JSON payload. This pattern (sometimes called API composition) exists to save clients from making four round trips themselves.
The real cost shows up gradually. Aggregation code has to decide which calls to make, in what order, how to handle a partial failure (do you return a degraded response if the recommendations service times out, or fail the whole request?), and how to merge fields that don't map cleanly onto each other. Every one of those decisions is a business rule, and business rules accumulating in the gateway is exactly what a gateway shouldn't be doing โ it's supposed to be plumbing, not a service in disguise. Once an aggregation endpoint starts encoding "if the user is a premium tier, also fetch X," it has quietly become an orchestration layer that no team distinctly owns, is hard to unit test in isolation from live services, and sits outside the normal service release cadence.
The Backend-for-Frontend (BFF) pattern
A single generic gateway trying to serve both a mobile app and a web app runs into a structural tension: mobile clients want minimal payloads and as few round trips as possible, because every extra request costs latency and battery on a cellular connection. Web clients, running on faster connections with more compute and screen real estate, can tolerate chattier calls and often want more data per request, not less.
Serve both from one contract and you end up compromising in both directions โ either the mobile client receives a bloated payload full of fields it discards, or the web client is starved of data and has to make follow-up calls to fill in what the shared response left out. Every schema change becomes a negotiation between two client teams with opposite needs.
The Backend for Frontend (BFF) pattern resolves this by giving each frontend type its own dedicated gateway/backend layer, shaped exactly for that client's needs and often owned by the team that owns the client. A mobile BFF might aggregate a screen's worth of data into one tightly-scoped response; a web BFF might expose a richer, more general-purpose API because the client can afford it. The trade-off is duplication โ similar aggregation and mapping logic gets written twice (or more, for a third-party BFF), and now there are more deployable services to operate, version, and keep secure. That duplication is usually worth it: coupling two client teams to one compromise-shaped contract tends to cost more in coordination overhead than the duplicated code costs in maintenance.
Where the gateway becomes a bottleneck or single point of failure
The operational risks of a gateway are easy to underweight because the pattern reads as "just infrastructure" on a diagram. In practice:
It becomes an unversioned monolith. Routes, auth rules, and aggregation logic accumulate over years, often maintained by a platform team disconnected from the service teams whose behavior they're routing around. Nobody wants to touch the gateway config because nobody fully understands what depends on it โ the exact failure mode microservices were supposed to eliminate has reappeared at the edge.
It adds latency to every single request. An extra network hop is unavoidable, and for aggregation endpoints the effective latency is bounded by the slowest of the fanned-out calls โ sometimes worse, if calls are sequential rather than parallel. Without per-call timeouts and circuit breakers, one degraded downstream service can drag down every aggregated endpoint that touches it, and now the gateway is amplifying a single service's bad day into a system-wide outage.
It has to be more available than anything behind it. Because every request passes through the gateway, its uptime puts a ceiling on the uptime of the entire system โ a gateway with 99.9% availability caps every service behind it at 99.9%, no matter how reliable those services are individually. That means the gateway layer needs to be horizontally scaled, stateless enough to add capacity on demand, and typically run active-active across zones or regions. It also needs deliberate capacity planning: connection pools to each backend service, thread or event-loop limits, and load testing that accounts for the fan-out multiplier of aggregation endpoints, not just raw request count.
A real-world example
Consider a company running around a dozen backend microservices โ catalog, pricing, inventory, cart, recommendations, reviews, user profile, and so on โ all originally exposed through one general-purpose gateway shared by the mobile app, the web app, and a handful of third-party integrators.
The mobile team notices their product detail screen makes eight to ten sequential calls through the gateway to render, each one adding latency on cellular networks and draining battery on background retries. They introduce a mobile BFF: one endpoint that fans out to catalog, pricing, inventory, and reviews in parallel and returns a single, mobile-shaped payload with only the fields the screen renders. Round trips for that screen drop from ten to one, and the mobile team owns the aggregation logic directly, so they can iterate on it without waiting on a shared platform team's release cycle.
The general-purpose gateway doesn't disappear โ it keeps serving third-party API consumers who need a broad, stable, well-documented REST surface with predictable versioning and per-partner rate limiting, none of which a narrow mobile-shaped BFF would satisfy. As the web app's feature set grows more distinct from mobile's, it eventually gets its own BFF too. Cross-cutting concerns โ token validation, global rate limiting, TLS termination โ stay centralized in a shared edge layer in front of all three: gateway proper, mobile BFF, and web BFF. This "gateway of gateways" layering is common once BFFs multiply: a thin outer edge for policy enforcement, and purpose-built BFFs behind it for shaping data.
Common mistakes to avoid
Letting business logic creep into aggregation code. Conditional rules based on user tier, feature flags, or domain-specific decisions belong in a service that's owned, tested, and versioned like any other โ not in gateway middleware that's hard to unit test against live dependencies and effectively owned by no one.
Treating the gateway as infinitely scalable. Fan-out multiplies load on backend services and connection pools in ways a simple request-count dashboard won't show. Capacity planning needs to account for the amplification factor of aggregation endpoints, not just raw throughput at the edge.
Using one shared gateway configuration for wildly different client needs. Cramming mobile-optimized and web-optimized (and partner-optimized) requirements into a single route set produces exactly the compromise-shaped contracts and deploy coupling the BFF pattern exists to avoid. If two client types keep pulling the shared config in opposite directions, that's the signal to split.
Frequently asked questions
Does a BFF per frontend just reintroduce duplication we removed by centralizing on a gateway?
Some duplication is real and expected โ similar mapping and aggregation logic often appears in more than one BFF. It's usually the cheaper trade-off compared to coupling multiple client teams to one contract that satisfies none of them well. Shared libraries for auth handling, tracing, and common client SDKs can absorb some of the duplication without re-merging the contracts themselves.
Should the gateway own authentication, or just enforce it?
Enforce, not own. The gateway is typically the place that validates tokens and applies authorization decisions โ checking a JWT signature, calling out to an identity provider, or evaluating a policy engine like OPA โ but the identity store and token issuance should live in a dedicated auth service. The gateway becomes a policy enforcement point, not a source of truth for who a user is.
When is a single generic gateway still the right call over BFFs?
Early-stage systems with one or two client types whose needs aren't meaningfully diverging yet, or systems where the operational overhead of running additional BFF services isn't justified by the payload mismatch. BFFs solve a real coordination problem, but they're not free โ introduce them when a specific client's needs are being visibly compromised by a shared contract, not preemptively.
None of these patterns are mutually exclusive, and most systems past a certain size end up running a layered combination: a thin, highly available edge for policy enforcement, purpose-built BFFs for client-specific shaping, and aggregation kept disciplined enough that it never quietly becomes the business logic layer no one meant to build. The pattern that matters isn't which one you pick โ it's recognizing, as your client base diversifies, when the gateway you already have has stopped fitting the job it's doing.
Keep Learning on ITVedas
One of many free guides across 8 IT chapters โ all in plain English.
Explore All Chapters โ