Backend for Frontend (BFF)
How the Backend for Frontend pattern creates purpose-built API layers for each client type, eliminating over-fetching and moving aggregation logic out of the client.
TL;DR
- A Backend for Frontend (BFF) is a thin, purpose-built API layer that sits between a specific client type and your backend microservices, returning exactly the data shape that client needs.
- BFF eliminates over-fetching for mobile and under-fetching for web by letting each client team own its own backend aggregation layer.
- BFF is not an API gateway. The gateway handles infrastructure (auth, rate limiting, routing). The BFF handles business logic (aggregation, response shaping, client-specific orchestration).
- The frontend team owns the BFF, not the backend team. This is the key organizational insight.
- When a single GraphQL endpoint can serve all clients well, you may not need separate BFFs at all.
The Problem
You have a mobile app and a web app. They call the same backend APIs. The web dashboard needs a full user profile with activity history, recent orders, and product recommendations, all aggregated from five microservices. The mobile home screen needs an avatar, a username, and a recent order count.
One generic API serves both. The mobile client downloads 10x more data than it displays, burning bandwidth and battery. The web client makes six sequential API calls to assemble a single page, adding 400ms of round-trip overhead. The frontend team ends up writing aggregation logic in JavaScript, merging responses from multiple services on the client side.
Now your "thin client" is doing backend work. Every new screen needs coordination across services. The iOS team and the web team both write the same aggregation code, differently, with different bugs. I've seen this pattern turn a 2-week feature into a 6-week multi-team coordination nightmare.
The fix is not "make the API return less." The fix is giving each client type its own purpose-built backend layer.
One-Line Definition
A Backend for Frontend creates a dedicated server-side layer per client type that aggregates, transforms, and shapes microservice responses into exactly what that client needs.
Analogy
Think of a restaurant with a diverse customer base: a diner ordering a full meal, someone grabbing takeout, and a delivery driver. The kitchen (microservices) makes the same food. But the diner gets it on plates with cutlery. Takeout gets it boxed with napkins and utensils. Delivery gets it sealed in bags with order slips.
The kitchen doesn't change. The packaging changes per consumer. The BFF is the packaging station, not the kitchen.
Solution Walkthrough
The BFF sits between the client and the microservices. Each client type gets its own BFF, and each BFF calls the same underlying services but returns a different response shape.
What each BFF does
The Web BFF aggregates data from all three services into a single rich response for the dashboard. It calls User, Product, and Order services in parallel, merges the results, and returns a composite JSON payload. The mobile BFF calls only User and Order, returns five fields, and omits product recommendations entirely.
BFF aggregation in action
Here's how the mobile BFF handles a home screen request:
The client gets a 200-byte response instead of 5KB. The BFF strips 95% of the payload. On a 3G connection, that is the difference between a 100ms load and a 2-second load.
BFF vs API Gateway
This distinction trips people up. Here is the clean separation:
| Concern | API Gateway | BFF |
|---|---|---|
| Authentication | ✅ Token validation, OAuth | ❌ Not its job |
| Rate limiting | ✅ Per-client throttling | ❌ Not its job |
| SSL termination | ✅ | ❌ |
| Request routing | ✅ Routes to correct BFF | ❌ |
| Response shaping | ❌ Passes through | ✅ Transforms and aggregates |
| Business logic | ❌ Zero business logic | ✅ Client-specific logic |
| Owned by | Platform/infra team | Frontend team |
The gateway is infrastructure. The BFF is product logic. They sit at different layers and are owned by different teams. For your interview: if someone asks "why not just use an API gateway," the answer is that gateways don't aggregate or transform, they route.
Who owns the BFF
This is the organizational insight that makes BFF work. The frontend team owns the BFF, not the backend team. The iOS team owns the Mobile BFF. The web team owns the Web BFF.
Why? Because the frontend team knows what data each screen needs. They know when the design changes. They know when a field is added or removed. If the backend team owns the BFF, every screen change requires a cross-team ticket, and you are back to the coordination problem BFF was supposed to eliminate.
My recommendation: if you cannot give BFF ownership to the frontend team, do not adopt the pattern. A BFF owned by the backend team becomes just another generic API layer.
Implementation Sketch
// Mobile BFF - Express.js handler for /home
app.get('/home', async (req, res) => {
const userId = req.auth.userId;
// Parallel calls to backend services
const [user, orders] = await Promise.all([
userService.getUser(userId),
orderService.getRecentOrders(userId, { limit: 3 }),
]);
// Shape response for mobile - only what the screen needs
res.json({
avatar: user.avatarUrl,
firstName: user.name.split(' ')[0],
recentOrders: orders.map(o => ({
id: o.id,
status: o.status,
})),
});
});
The BFF is intentionally thin. No database of its own, no persistent state. It is a translator and aggregator. If your BFF starts growing its own data model, that is a smell.
When It Shines
- Multiple client types with wildly different data needs. Web dashboard vs mobile app vs IoT sensor.
- Frontend teams that ship independently. The iOS team can update their BFF without waiting on backend changes.
- High-latency clients (mobile, IoT). A single BFF call replaces multiple round trips. On a 500ms-RTT cellular connection, going from 6 calls to 1 saves 2.5 seconds.
- Legacy backend with chatty APIs. The BFF shields clients from needing 14 calls to build a single screen.
- Different auth flows per client. Web uses cookie sessions, mobile uses refresh tokens, TV uses device pairing codes. Each BFF handles its own auth concern.
Failure Modes & Pitfalls
BFF proliferation
You start with Web BFF and Mobile BFF. Then someone adds Tablet BFF, Watch BFF, Partner BFF, Internal-Tool BFF. Each needs maintenance, deployment pipelines, and monitoring. The rule of thumb: one BFF per experience, not per device. A tablet that shows the same screens as the web app uses the Web BFF.
BFF becomes a monolith
The BFF accumulates business rules, validation, caching, and eventually its own database. Now you have a second monolith sitting between clients and services. Keep BFFs stateless. If logic belongs in a service, push it down.
Shared logic duplication
The Mobile BFF and Web BFF both need the same "calculate order total with tax" logic. You copy it. Now you have two copies that drift. Extract shared logic into a shared library or push it into the Order Service itself.
BFF as a crutch for bad APIs
If your microservices have poorly designed APIs, adding a BFF hides the mess instead of fixing it. You should still fix the underlying APIs. The BFF handles client-specific shaping, not compensating for bad backend contracts.
Single point of failure
If the Mobile BFF goes down, every mobile user is offline. Each BFF needs its own health checks, circuit breakers, and deployment redundancy.
Trade-offs
| Pros | Cons |
|---|---|
| Each client gets optimized payloads | More services to deploy and monitor |
| Frontend teams own their backend layer | Risk of duplicated logic across BFFs |
| Clients decouple from microservice boundaries | Added network hop (client to BFF to services) |
| Independent deployment per client type | BFF can become a dumping ground for logic |
| Simpler client code (no aggregation) | Requires mature DevOps (CI/CD per BFF) |
The fundamental tension is client optimization vs operational complexity. Every BFF you add reduces client-side complexity but increases server-side surface area.
Real-World Usage
SoundCloud pioneered the BFF pattern. Their mobile app and web player had fundamentally different data needs from the same backend. They introduced separate BFFs per client, each owned by the respective frontend team. This became one of the canonical case studies for the pattern.
Netflix runs distinct API layers for TV, mobile, and web. Each device category has different screen resolutions, interaction patterns, and data requirements. Their personalization data alone differs per device: a TV shows large artwork tiles, while mobile shows compact lists. Netflix processes over 250M concurrent streaming sessions, each routed through device-specific edge services.
Spotify uses BFF-style layers to serve different client experiences. The desktop app, mobile app, and embedded car player all need different data densities for the same underlying catalog. Their "Home" screen on mobile aggregates from 10+ microservices into a single response, shaped by a mobile-specific backend layer.
How This Shows Up in Interviews
BFF comes up in two contexts: (1) system design for multi-platform products, and (2) API design discussions where the interviewer probes how you handle different client needs.
When to bring it up: Whenever the problem involves more than one client type (web + mobile, or consumer app + partner API). Say: "I'd introduce a BFF per client type so each team can shape their own API contract."
Depth expected at senior/staff level:
- Explain the ownership model (frontend team owns the BFF)
- Distinguish BFF from API gateway clearly
- Know when GraphQL replaces the need for separate BFFs
- Discuss BFF proliferation and when NOT to use it
| Interviewer asks | Strong answer |
|---|---|
| "Why not just have the API return different fields per client?" | "Content negotiation gets messy fast. The BFF gives each client a clean contract without polluting the API with per-client conditionals." |
| "Isn't this just another layer of complexity?" | "Yes, and it is only worth it when you have 2+ client types with genuinely different data needs. For a single client, skip BFF entirely." |
| "How do you handle shared logic across BFFs?" | "Shared libraries for pure functions, or push the logic into the backend service. Never duplicate business rules across BFFs." |
| "What about GraphQL instead of BFF?" | "GraphQL is essentially a programmable BFF. It works when all clients share the same domain model. Separate BFFs are better when clients need entirely different response structures." |
GraphQL as BFF replacement
GraphQL deserves its own callout here. A well-designed GraphQL API with per-client queries eliminates much of what a BFF does: the client declares exactly what it needs, the server resolves it. If your backend services already expose a unified GraphQL schema, you might not need separate BFFs.
But GraphQL does not replace BFF when: (1) clients need different auth flows, (2) you need server-side orchestration that goes beyond simple field resolution, (3) your backend speaks REST/gRPC and adding a GraphQL gateway is more complexity than adding thin BFFs.
Quick Recap
- A Backend for Frontend (BFF) creates a purpose-built API layer per client type, eliminating over-fetching for mobile and under-fetching for web by shaping responses at the server level.
- The BFF aggregates calls to multiple microservices and returns a single, optimized response per screen.
- BFF is product logic (response shaping, aggregation). API gateway is infrastructure (auth, routing, rate limiting). They are different layers.
- The frontend team owns the BFF, and this ownership model is the key organizational benefit that prevents cross-team coordination bottlenecks.
- Create one BFF per experience (web, mobile, TV), not per device (iOS, Android, tablet, watch), to avoid proliferation.
- GraphQL can replace separate BFFs when all clients share the same domain model, but falls short when clients need different auth flows or have constrained runtimes.
- A healthy BFF is stateless, under 2,000 lines, and can be rebuilt in a day. If it has a database, it is no longer a BFF.
Related Patterns
- API Gateway - The infrastructure layer that sits in front of BFFs, handling auth, rate limiting, and routing. BFF and gateway are complementary, not competing.
- Microservices - BFF assumes a microservices backend. If you have a monolith, BFF adds a layer without the benefit of service-level decomposition.
- REST vs GraphQL - GraphQL is effectively a programmable BFF. Understanding when GraphQL suffices eliminates the need for separate BFF layers.
- Strangler Fig - BFF can serve as a strangler facade during migration, intercepting client requests and gradually routing them to new services while maintaining the old API contract.
Good fit for BFF:
- Multiple client types with significantly different data needs (mobile vs. web vs. admin)
- Client teams own the full stack — they can own the BFF as part of their team's responsibility
- Aggregation logic that would otherwise live in the client itself
- Per-client authentication and authorization differences
Avoid BFF when:
- You have one client type — a single shared API is simpler
- The frontend team can't own and deploy the BFF — if it becomes a shared service, you lose the independence benefit
- You'd end up with a BFF that's identical to what a simple API gateway could provide
- Duplication across BFFs creates a maintenance burden — if three BFFs all implement the same cart logic, a bug is fixed in one and missed in two
Ownership Model
BFF is most effective when the client team owns the BFF. The web team owns the web BFF. The mobile team owns the mobile BFF. They control their own release cycles, can update their API contract without coordinating with other teams, and are incentivized to keep the BFF thin and fast.
When BFF is owned by a platform team serving all clients, it tends to drift into a second API layer with the same coordination problems as a shared API.
Quick Recap
- BFF creates a purpose-built API layer per client type — instead of one API that over-fetches or requires clients to aggregate from multiple services, each BFF provides exactly the shape its client needs.
- BFF is not an API gateway. An API gateway handles routing, auth, and rate limiting for all clients generically. A BFF handles data aggregation and response shaping specific to one client type.
- GraphQL is a natural fit for BFF: the client declares what fields it needs, the resolver calls internal services to populate those fields, and no over-fetching occurs.
- BFF works best when the client team owns the BFF — they control their release cycle and API contract without coordinating with other teams. When a platform team owns all BFFs, the coordination benefits disappear.
- Avoid BFF when you have a single client, when BFF logic would be duplicated across all client variants, or when the team can't own and deploy the BFF as part of their stack.