Backend-for-Frontend (BFF) Pattern: When and Why
If you've built one API that serves a web app, a mobile app, and a partner integration, you've probably felt the same friction. The mobile team wants a smaller payload, the web team wants a different shape, and every change has to satisfy all three at once. The backend-for-frontend pattern exists to remove that friction by giving each client its own tailored API instead of forcing everyone through one general-purpose backend. It's one of several recurring answers to questions covered in API architecture: patterns, principles, and modern approaches, and it pairs closely with a neighboring pattern: the shift toward microservices that usually creates the multi-client problem in the first place.
What Is the Backend-for-Frontend (BFF) Pattern?
Backend for frontend (BFF) is an architectural pattern in which each type of client (web, mobile, or a third-party integration) gets its own dedicated backend, shaped around what that specific client needs, instead of every one of these frontend clients sharing a single general-purpose backend. Each BFF sits between its client and the shared downstream services, tailoring requests and responses to that client's data and format requirements.
The pattern grew out of work at SoundCloud, where Phil Calçado named it the Backend For Frontend. Its standard write-up is by Sam Newman under the plural name "backends for frontends." It has since become a standard reference point in microservices design discussions. It isn't a niche technique, but one of the default answers to "how do we serve very different clients from the same set of services?" Newman's own framing is worth keeping in mind as you read the rest of this piece. BFFs solve a pressing concern for mobile development under microservices, and they work as a compelling alternative to a general-purpose API backend more broadly, well beyond mobile.
The Problem: Why One General-Purpose API Backend Doesn't Scale
Most applications start with one client and one backend built to match it. A team ships a desktop web app, and the backend grows up alongside it: the same data model, same response shapes, same assumptions about what the caller needs. That arrangement works fine right up until a second client shows up with different needs.
Then a mobile app shows up. Mobile clients don't have the same constraints as a desktop browser. Smaller screens mean smaller payloads make sense, cellular connections make chatty request patterns expensive, and battery life makes every unnecessary round trip a cost. A backend originally built for a desktop UI now has to serve two audiences with different needs, and it usually resolves that tension by adding conditional logic (special-cased fields, optional parameters, version flags) until the "general-purpose" API backend is really a backend with several personalities stitched together.
Add a third client, like a partner integration that only needs a narrow slice of your data in a completely different shape, and the pattern compounds. Every new client type adds branching logic to a shared codebase, and every change has to be checked against every client it might affect. The team owning that shared backend becomes a bottleneck between every frontend team and the change they're trying to ship, because nobody can touch the general-purpose backend without thinking about who else depends on it.
At some point, the honest fix stops being "add one more conditional" and starts being "give this client its own separate backend." That's the moment teams start looking at BFF not as an upfront architectural choice made on day one, but as the response to a general-purpose API backend that has accumulated enough client-specific branching that no single change is safe to make anymore.
How the BFF Pattern Works
Instead of one backend serving every client, BFF creates one client-specific API layer per client type, commonly web, mobile, and third-party or partner integrations. Each of these tailored APIs sits in front of the same set of downstream services (accounts, catalog, orders, or whatever the domain requires) and shapes the response for its specific consumer.
It's worth being precise about the traffic flow here. The client-to-BFF-to-backend hop is a straightforward request chain, but it's easy to stretch that picture into something the pattern doesn't promise: one fixed sequence of downstream calls that every request marches through. The client calls its client-specific API. That API then calls whichever downstream services the request actually needs, not a fixed sequence, but whatever subset of services answers that particular request, in whatever order or parallelism makes sense. A mobile dashboard request might pull from three services at once, and a partner's single-resource lookup might touch only one. The downstream services are independent of each other and of the BFF. The BFF's job is orchestration and shaping, not gluing them into one mandatory pipeline.

This is also where BFF earns its "not just an API gateway" distinction. A generic API gateway routes and secures traffic to whatever backend a path maps to. A BFF goes further as it reshapes the response specifically for one client type, which is a content and structure decision, not just a routing one. A well-known variant of this trade-off comes up constantly in practice. Many teams reach for GraphQL specifically because a single, well-designed schema lets each client ask for exactly the fields it needs, which reduces the pressure to stand up separate BFFs for every client in the first place.
BFF and API Security: Token Handling
BFF shows up in a second, distinct context that has nothing to do with mobile versus desktop payload shapes: OAuth 2.0 and OpenID Connect token security. Developers searching for a "BFF API" pattern are often looking for this security-focused variant specifically, rather than the client-shaping use case above.
OAuth 2.0 defines two categories of client applications. A confidential client can keep its credentials secret, typically because it runs on a server the attacker can't reach. A public client, such as a single-page application running in the browser or a native mobile app, can't reliably keep a secret, because anyone with access to the browser's dev tools or the device's file system can inspect it. That distinction matters because an access token or refresh token held directly in browser-accessible storage is exposed to any script that runs on the page, including a script injected through a cross-site scripting flaw.
Native apps are public clients under this same classification, but they don't run inside a browser, so the browser-cookie mechanism described below doesn't apply to them directly. A native app still needs its own secure way to authenticate. Native apps typically don't need a BFF for token custody: following RFC 8252, they run the authorization code flow with PKCE through the system browser and keep tokens in the device's own secure storage (its keychain or keystore). A BFF narrows where tokens live, but it doesn't remove the client's own need to authenticate securely.
The BFF pattern gives a browser-based application a way to behave like a confidential client without becoming one itself. Instead of the single-page app requesting and holding tokens, a server-side component, the BFF, takes on that role. It interacts with the authorization server as the confidential client, holds the resulting access token and refresh token on the server, and gives the browser only a session identifier. That session cookie needs its own protections, not encryption. The HttpOnly attribute stops JavaScript from reading it, the Secure attribute keeps it off unencrypted connections, an appropriate SameSite setting limits when it's sent cross-site, and the BFF still needs explicit CSRF defenses on top, since a cookie is attached to requests automatically by the browser. Getting this combination right is what the OAuth 2.0 for Browser-Based Applications (RFC 10017) best current practice addresses directly. It's also worth being precise about what this does and doesn't stop. HttpOnly and Secure keep the cookie from being read or intercepted, but they don't stop an XSS payload from riding the session that cookie is already attached to. A BFF removes token theft as a risk without removing every consequence of an XSS vulnerability on the page.
The browser never sees a bearer token it could leak. It just asks its own BFF for data, and the BFF attaches the real access token to the downstream call. This security-oriented BFF layer is deliberately thin. Its job is token custody and session translation, not client-specific data shaping, which is why it's worth treating as a distinct use case from the payload-shaping BFFs described above.
This is a genuinely different motivation for building a BFF than the per-client-shape problem in the sections above, and it's worth keeping the two separate in your head. One is about tailoring payloads to a client type and the other is about where a sensitive credential is allowed to live. A single BFF can address both at once, but a team that only needs the security property doesn't need three client-shaped APIs to get it. A single BFF in front of the browser app, handling tokens, solves that problem on its own.
When to Use the Backend-for-Frontend Pattern
BFF earns its complexity when the signals below show up together, not because a checklist says so in isolation.
- Client needs are genuinely diverging: Web, mobile, and partner clients want different data shapes, different payload sizes, or different pagination and caching behavior, and a shared backend keeps growing branching logic to satisfy all of them at once.
- A frontend team needs to move independently: If shipping a mobile feature currently means opening a ticket against a shared backend team and waiting for a release that also has to satisfy the web team, a dedicated BFF gives that team a layer they can change on their own schedule.
- Tokens need to come off the browser: If you're securing a single-page app with OAuth/OIDC and want to stop holding bearer tokens client-side, a security-focused BFF addresses that directly, independent of any payload-shaping need.
- You're aggregating data from several services for one screen: When a single view needs data assembled from multiple downstream services and that assembly logic doesn't belong in any one of them, a client-specific API layer is a reasonable place to put it.
When Not to Use It
BFF isn't free, and the honest costs are worth naming before adopting it.
Code duplication is a real and likely cost. Authentication checks, logging, and common data-shaping logic tend to get re-implemented across every BFF you stand up, because each one is a separate service with its own codebase. Some of this can be pulled into a shared library, but shared libraries between BFFs reintroduce some of the coordination overhead the pattern was meant to avoid.
A single client type doesn't automatically need this pattern for payload shaping. If payload shaping is the only goal and there's only one frontend with no near-term plan to add a second client type, a dedicated BFF is a layer of indirection solving a problem you don't have yet. The token-isolation case above is the exception: a single-client app can still justify a BFF purely to keep tokens off the browser, independent of how many client types it serves.
Operational overhead scales with the number of BFFs. Every BFF is a service to deploy, monitor, and keep patched. Teams that skeptically ask whether BFF is still worth it in a given architecture are usually pointing at this cost directly: more services in production, each needing its own on-call attention, for a benefit that a shared, well-designed API might have delivered more cheaply.
Business logic creeping into a BFF is a maintenance trap. A BFF is meant to orchestrate and shape data for one client, not own domain rules. If business logic that should live in a downstream service starts accumulating in a BFF because it's convenient, that logic becomes tied to one client's release cycle and invisible to every other client that needs the same rule applied consistently.
Practitioner discussion of this pattern tends to circle back to the same question: is it still worth the extra services once GraphQL or a well-designed general-purpose API can serve most clients directly? There's no universal answer, but the pattern in the debate is telling. Teams that end up glad they built a BFF usually point to a security requirement (tokens off the browser) or a genuinely divergent client, not payload convenience alone. Teams that regret it usually built one for a client difference that turned out to be minor, or before they had a second client type at all.
Building BFFs With an API Gateway
Standing up a BFF has traditionally meant writing and deploying a new backend service for every client type: its own codebase, its own deployment pipeline, its own on-call rotation. That's a real commitment, and it's the main reason teams hesitate even when the client-shape problem is genuine.
A modern API gateway's policy engine gives you a lighter-weight path to the same outcome for a meaningful share of BFF use cases. Rather than a bespoke server-side application per client, you shape a client-specific API product directly at the gateway. That means request and response transformation, header manipulation, and routing rules that differ by consumer, chained together as policies on the same underlying API. WSO2's API Platform, for example, runs authentication, rate limiting, and payload-transformation policies at the API or resource level, and multiple policies can be chained on a single operation, enough to reshape a response for a given client type without a separate service sitting behind it. It's worth being precise about the ceiling here. This covers transformation, filtering, and routing well. Assembling data from several independent backend calls into one response is a heavier lift that a full custom BFF, or a gateway with an explicit orchestration/aggregation capability, handles more directly.
That ceiling extends to the security-focused use case from the section above, too. Gateway policies can enforce authentication on the way in, but they don't take on the OAuth confidential-client role, hold a user's tokens, manage a browser session cookie, or run CSRF and logout on an application's behalf. Those responsibilities stay in the BFF's own application code. A gateway secures and shapes the traffic reaching that BFF, but it doesn't replace it.
| Custom BFF service | Gateway-shaped API product | |
|---|---|---|
| What it is | A dedicated backend you write and deploy per client | A client-specific view configured as policies on an existing API |
| Best fit | Complex per-client logic, heavier aggregation, or a security-focused BFF holding tokens server-side | Payload shaping, filtering, and routing differences that don't need custom code |
| Ongoing cost per client | A service to build, deploy, and operate | Configuration to maintain on an API you're already running |
Neither approach replaces the other outright, and the choice isn't really "API gateway versus BFF." A gateway is one of the ways a BFF gets built, not a competing pattern. Teams that do need a full custom BFF, say for the token-handling case described above, still benefit from running it behind the same gateway as everything else. Consistent authentication, rate limiting, and observability apply whether a client is served by a lightweight gateway-configured API product or by a dedicated BFF service sitting behind it.
That's the practical value of putting both under one control plane rather than treating gateway-level API management and BFF services as two separately governed systems. It also holds up in the messier, real-world case where not every gateway in the estate is the same product. A custom BFF service deployed behind a third-party gateway, or a BFF fronting an API discovered from a federated gateway elsewhere in the organization, can still be governed from the same place rather than needing a separate policy set per gateway vendor.
The pattern choice (full custom BFF, gateway-shaped API product, or a mix depending on the client) sits underneath that governance layer rather than in competition with it. It's one piece of a broader set of decisions, including how federated gateways get discovered and governed in the first place, which the API gateway architecture spoke in this series covers directly.
Conclusion
BFF isn't a single decision so much as two separate ones wearing the same name: whether your clients' data needs have actually diverged enough to justify a dedicated layer, and whether a token needs to come off the browser entirely. Answer those honestly before you commit to a full custom service for every client type. A gateway's policy engine can cover a meaningful share of the shaping work without a new service to operate, and it's worth trying that route first for anything short of heavy aggregation or server-side token custody. Reach for a full custom BFF when the client difference or the security requirement is real, not because the pattern is popular.
Frequently Asked Questions
Is a BFF the same thing as an API gateway?
An API gateway is a general entry point that routes, authenticates, and secures traffic to whatever backend a request maps to. A BFF goes a step further for one specific client, reshaping the response content itself. A gateway's policy engine can be used to build a lightweight BFF, but the two are complementary concepts, not synonyms.
Is the backend-for-frontend pattern still relevant today?
Yes, though it's worth applying deliberately rather than by default. It solves a real problem (diverging client needs and token security), but it adds services to operate. Teams sometimes reach for a full custom BFF when a gateway's transformation policies would have solved a narrower version of the same problem more cheaply.
How many BFFs should an application have?
Typically one per client type that has genuinely different needs, not one per platform. Sharing one mobile BFF between iOS and Android is a common choice when both apps deliver the same experience, as SoundCloud did. However, the guideline Sam Newman highlights is "one experience, one BFF," and he cautions that a BFF serving more client types is more likely to bloat.
Does a BFF replace the need for an API gateway?
A BFF still benefits from sitting behind a gateway for the cross-cutting concerns (authentication, rate limiting, and observability) that apply across every client, BFF or not.
Can a BFF hold business logic?
A BFF is meant to orchestrate calls and shape responses for its client. Domain rules that need to apply consistently across clients belong in the downstream services, not duplicated inside a client-specific layer.