The Backend For Frontend pattern, shortened to BFF, is a server layer dedicated to a single type of client. Rather than letting a web or mobile app call a dozen APIs and stitch the data back together itself, you slot in a service that does that work on its behalf, in the exact shape it needs. The idea was born at SoundCloud around 2015, when the teams realized a single API couldn't serve both their website and their mobile apps without turning into a monster nobody could evolve.
What a BFF actually is
Picture a typical setup: a website, an iOS app, an Android app, and behind them a microservices architecture — one service for accounts, one for the catalog, one for payments, one for notifications. Without a BFF, every front-end has to know all these services, call them in the right order, aggregate the responses and reshape them. The browser spends its time orchestrating network calls instead of rendering the interface.
With a BFF, the front-end only ever talks to one counterpart. It sends a request, the BFF calls whichever services it needs, assembles the result and returns a response tailored to that client. The key point: one BFF per client experience. The web BFF and the mobile BFF don't return the same thing, because a phone screen doesn't have the same needs as a desktop dashboard. The mobile app wants a light payload already trimmed for a small screen; the web can afford richer data.
The problem it solves
Three pain points come up every time the front-end talks straight to the APIs.
Over-fetching and under-fetching: a general-purpose API returns either too much data (the client downloads a full object to display three fields) or not enough (it has to chain several calls to rebuild one screen). Either way, the user waits. A BFF returns exactly the fields the screen needs, no more, no less.
Coupling: when the front-end knows the structure of every microservice, the smallest change to a back-end API risks breaking the interface. The BFF absorbs those changes: services evolve behind it, while the contract with the front-end stays stable.
Aggregation logic in the browser: stitching five JSON responses together, handling partial failures, retrying a failed call — that's code the browser runs on the user's device, on their battery and their connection. Moving it to the server makes it faster and more predictable.
BFF, API Gateway, GraphQL: don't mix them up
This is the most common confusion. All three touch client-to-service communication, but they don't play the same role.
An API Gateway is a single entry point for all clients. It centralizes authentication, rate limiting, routing. Its weakness: if the mobile app wants slim responses and the web wants full ones, the gateway becomes a shaky compromise — a "God object" everyone has to share.
A BFF does the opposite: it's dedicated to one client. It doesn't try to please everyone; it serves a specific front-end and optimizes it fully. You can even combine both: a gateway up front for cross-cutting concerns, then one BFF per client type behind it.
GraphQL attacks the same problem from another angle: the client decides which data it wants, through a query. It solves over-fetching without writing an endpoint per screen, but it brings its own complexity (guarding against overly deep queries, caching that's harder than in REST). A BFF can actually expose GraphQL internally.
In practice: a gateway if all your clients want the same thing, a BFF if their needs diverge, GraphQL if your data model is highly interconnected and you want to give the front-end autonomy.
A BFF keeps your access tokens safe
This is the angle people often forget. A single-page application (React, Vue) talking directly to APIs has to store its access tokens somewhere in the browser. But anything the browser's JavaScript can read, an XSS attack can steal too.
The BFF breaks that setup. The token never leaves the server: the front-end authenticates against the BFF, which sets an httpOnly session cookie — one that JavaScript cannot read. On every request, the BFF picks up the token server-side and injects it into the call to the API. The browser never sees the access token. That's the foundation of the BFF pattern recommended today for SPAs consuming OAuth flows: tokens stay server-side, in a signed and encrypted cookie, out of reach of a malicious script.
One caveat, though: httpOnly stops a script from reading the token, not from abusing it. An XSS can still fire requests on the user's behalf — the browser attaches the cookie automatically. The BFF removes token theft, not the XSS flaw itself: you still have to sanitize inputs and guard against CSRF.
Next.js sharply lowers the cost of entry
If you work with Next.js, you already have the tooling at hand. Route Handlers (the app/api/ folder) and Server Components run server-side, in the same project as your front-end. Writing a BFF doesn't require deploying a separate service: it's a route inside your app.
import { cookies } from "next/headers";
export async function GET() {
const token = (await cookies()).get("session")?.value;
// The BFF calls several services and assembles the response
const [profile, projects, invoices] = await Promise.all([
fetch(`${process.env.API}/me`, { headers: { Authorization: `Bearer ${token}` } }),
fetch(`${process.env.API}/projects`, { headers: { Authorization: `Bearer ${token}` } }),
fetch(`${process.env.API}/invoices`, { headers: { Authorization: `Bearer ${token}` } }),
]);
return Response.json({
profile: await profile.json(),
projects: await projects.json(),
// return only what the dashboard needs
invoices: (await invoices.json()).slice(0, 5),
});
}The front-end makes a single fetch("/api/dashboard"), the token stays in the httpOnly cookie, and the aggregation logic lives server-side. That's a BFF, with no new infrastructure. It's also why the line between "front" and "back" has blurred: on the back side of that kind of service, a framework like FastAPI or your existing microservices still expose the business logic, while the BFF just translates.
A security note on the example: to be truly clean, the cookie shouldn't carry the raw access token like it does here. The tidiest approach is an opaque session cookie — just an identifier — with the real token stored server-side (in a session or a Redis-style cache). Failing that, a carefully encrypted and signed cookie.
And beware the "almost free" shortcut. The code is easy to write, not to operate. In production, the real work is timeouts, retries, logs, monitoring, caching, partial errors, cookie security and serverless limits (execution duration, cold starts). Next.js lowers the cost of entry; it doesn't remove the cost of running the thing.
What a BFF gives you
The most visible win is perceived speed. Each client receives data already shaped for it, so fewer calls and fewer bytes on the wire, so an interface that renders faster. Next comes team decoupling: the front-end team that owns its BFF evolves it without waiting on the back-end team, and each BFF stays small enough to fit in one head, whereas a shared back-end grows without end. You also get handy fault isolation, since a problem on the mobile BFF doesn't touch the web, and you can take one down or redeploy it without the other. Last, security: secrets and tokens stay server-side, never exposed to the browser.
The pitfalls to avoid
A BFF isn't free, and misused it turns against you.
Don't put business logic in it: this is the number-one anti-pattern, though the line deserves nuance. A BFF legitimately holds presentation and orchestration logic — aggregating, reshaping, filtering, deciding which services to call. What it must not hold is durable business rules, direct database writes, and decisions that belong to the domain. The moment it crosses that line, you're recreating the monolith you set out to avoid. Those rules stay in the back-end services.
Code duplication: two BFFs often end up sharing similar transformations. Trying to factor everything into a shared service drags you back toward the gateway's God object. A bit of duplication is the price of team autonomy, not a flaw to fix at all costs.
The extra network hop: a BFF adds a middleman, so some latency. Well optimized (parallel calls, caching), the effect is negligible and more than offset by the drop in client-side calls. Done badly, it becomes a bottleneck.
Layer sprawl: one BFF per client means services to deploy, monitor, secure and version. On a small team with a single front-end, that cost often outweighs the benefit.
When to adopt it, when to skip it
A BFF makes full sense when you have several clients with genuinely different needs: a dense web dashboard, a stripped-down mobile app, a kiosk, an extranet portal for your customers. It also shines whenever token security becomes critical, or when your front and back teams want to move at different speeds.
Conversely, if you're building an app with a single front-end consuming data in the same shape the back-end produces it, a single API stays simpler and cheaper to maintain. Adding a BFF "just in case" means paying for complexity you don't need yet. As so often in architecture, the right question isn't "is this a nice pattern" but "what concrete problem am I solving today". On a product like a multi-platform SaaS, the answer leans toward a BFF fast; on a single-interface business software, rarely.
In practice, five questions settle it:
- Several clients with genuinely different needs?
- Too many API calls to orchestrate on the front-end?
- Access tokens exposed to the browser?
- Front and back teams moving at different speeds?
- Operating costs (deployment, monitoring, security) you can accept?
Several "yes" and a BFF is probably worth it. Mostly "no": keep a single API.
In short
The Backend For Frontend moves the aggregation and shaping work from the browser to a server service dedicated to each client. It makes interfaces faster, tokens safer and teams more autonomous — provided you keep it thin, free of business logic, and only reach for it when several clients with diverging needs justify it. For a single interface, a good API is enough. As soon as the front-ends multiply, the BFF becomes one of the most cost-effective patterns for keeping your architecture from turning into a plate of spaghetti.




