Backend for Frontend (BFF)#
Problem statement (interviewer prompt)
A web app, iOS app, and partner API all need to render the same product page but with different field subsets, image sizes, and call patterns. A single one-size-fits-all backend ends up over-fetching for some and under-fetching for others. Design a layer that adapts the same backend services to each client.
The BFF pattern places one thin aggregation service per client type. Each BFF speaks the client's preferred shape (GraphQL for web, REST for mobile, gRPC for partners) and fans out to the same underlying microservices.
flowchart TB
W([Web app]) --> BW[Web BFF]
M([Mobile app]) --> BM[Mobile BFF]
P([Partner API]) --> BP[Partner BFF]
BW --> Cat[Catalog service]
BW --> Inv[Inventory service]
BW --> User[User service]
BM --> Cat
BM --> Inv
BM --> User
BP --> Cat
BP --> Inv
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class W,M,P client;
class BW,BM,BP edge;
class Cat,Inv,User service;
Each BFF is owned by the client team; rendering churn doesn't propagate into the shared services.
Introduced by SoundCloud and popularised by Sam Newman; the BFF is the practical answer to "one backend, many very different clients."
The shape#
flowchart TB
subgraph Clients
W([Web SPA])
M([Mobile])
TV([TV])
PA([Partner])
end
subgraph BFFs
BW[Web BFF<br/>GraphQL or REST]
BM[Mobile BFF<br/>compact REST]
BT[TV BFF<br/>tile + carousel shape]
BP[Partner BFF<br/>versioned REST + auth]
end
subgraph Domain[Domain services]
Cat[Catalog]
Order[Orders]
User[Identity]
Rec[Recommendations]
end
W --> BW
M --> BM
TV --> BT
PA --> BP
BW --> Cat
BW --> Order
BW --> User
BW --> Rec
BM --> Cat
BM --> Order
BT --> Cat
BT --> Rec
BP --> Cat
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class W,M,TV,PA client;
class BW,BM,BT,BP edge;
class Cat,Order,User,Rec service;
What lives in a BFF#
- Aggregation: a single client call fans out to many backend calls in parallel.
- Shape transformation: pick fields, rename, flatten, embed.
- Authentication translation: external token (Google OAuth) → internal JWT (mTLS, signed identity).
- Caching: per-client response cache; mobile gets longer TTL.
- Light feature logic: A/B test routing, feature flag wiring, locale.
- Throttling: per-client rate limits separate from backend limits.
What does NOT live in a BFF#
- Business rules — they belong in the domain service.
- Persistent state — BFF is stateless.
- Cross-client logic — duplicate or push to a shared service.
BFF vs API gateway#
| BFF | API gateway | |
|---|---|---|
| Specialised per client | Yes | No |
| Aggregates / shapes responses | Yes | Limited |
| Auth / rate limit | Yes (per client) | Yes (cross-cutting) |
| Generic policy enforcement | No | Yes |
| Common pattern | Web + mobile + partner | Multi-tenant API exposure |
They compose: API gateway at the very edge → BFF per client → backend services.
flowchart LR
Client --> GW[API gateway<br/>auth, rate limit, WAF]
GW --> BFF[Client-specific BFF<br/>aggregate, shape]
BFF --> Svc[Domain services]
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class Client client;
class GW edge;
class BFF,Svc service;
Ownership#
The client team owns its BFF. The cycle is: client devs add a field → push a BFF release → ship client → no backend service touched. Eliminates cross-team coordination on rendering churn.
When to skip BFF#
- One client (web only): just expose a REST API.
- Public API as product (Stripe, Twilio): the API IS the product.
- GraphQL across the board: a federated GraphQL gateway can serve many shapes from one endpoint.
Pitfalls#
- BFF turns into a god service: starts owning business rules. Refactor logic into backend services.
- Duplicate logic across BFFs: extract into a shared library or a federated layer.
- Over-fetching from backends: BFF must use efficient queries (GraphQL federation, gRPC streaming).
- N+1 calls inside BFF: parallelise upstream fan-out with DataLoader-style batching.
How BFF composes#
flowchart TB
BFF((BFF<br/>pattern))
GW[API gateway<br/>at the edge]
REST[REST API design<br/>typical wire format]
GRPC[gRPC mechanics<br/>backend transport]
CACHE[Caching strategies<br/>per-client response cache]
GW --> BFF
BFF --> REST
BFF --> GRPC
BFF --> CACHE
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class BFF service;
class GW,REST,GRPC,CACHE datastore;
Glossary & fundamentals#
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
API Gateway | edge layer in front of BFF | api-gateway |
LLD |
REST API design | typical BFF wire format | rest-api-design |
HLD |
gRPC mechanics | backend transport for BFF | grpc-mechanics |
HLD |
Caching strategies | per-client response cache | caching-strategies |
Quick reference#
Idea#
One thin aggregation service per client type (web / mobile / TV / partner). Each BFF fans out to the same domain services and serves the client its preferred shape.
What BFF does#
- Fan out to backends in parallel
- Shape / project / rename
- Translate auth (OAuth → internal JWT)
- Per-client cache + rate limit
- Light feature flag / A/B routing
What it does NOT do#
- Business rules (live in domain)
- Persistent state
- Cross-client logic
BFF vs API gateway#
| BFF | Gateway | |
|---|---|---|
| Per client | Yes | No |
| Aggregate | Yes | Limited |
| Cross-cutting (WAF, rate) | Partial | Yes |
Compose: client → API gateway → BFF → services.
Ownership#
Client team owns its BFF; backend churn doesn't block client launches.
Skip BFF when#
- Single client app
- Public API as product
- Federated GraphQL serves all shapes
Pitfalls#
- BFF turning into god service
- Duplicate logic across BFFs
- N+1 fan-out (use DataLoader batching)
Refs#
- Sam Newman - BFF pattern
- SoundCloud BFF post
- Microsoft architecture docs
FAQ#
What is the Backend for Frontend pattern?#
BFF places a thin per-client aggregation service in front of shared backend microservices. Each BFF tailors data shape, payload size, and call patterns to one client like web, iOS, or partners.
BFF pattern vs API gateway, what is the difference?#
An API gateway is shared across many clients and focuses on auth, routing, and rate limiting. A BFF is per-client and contains business aggregation logic specific to that client's screens.
When should I use the BFF pattern?#
Use BFF when multiple clients need different views of the same backend services, or when mobile and web require different payload sizes and call patterns. Skip it for simple single-client products.
Who owns the BFF code?#
Best practice is that the frontend team owns its BFF so they can move at frontend speed without backend coordination. The BFF acts as their personal backend, not a shared platform.
Does GraphQL replace BFF?#
GraphQL solves over-fetching and under-fetching at the protocol level, often used to implement a BFF. You still need per-client business logic, so the BFF concept survives even when the transport is GraphQL.
Related Topics#
- API Gateway: generic edge layer that often fronts BFFs
- REST API Design: what a BFF typically exposes to clients
- gRPC Mechanics: the inbound-to-domain transport BFFs use