Backend for Frontend Pattern
Backend for Frontend (BFF) is a microservices design pattern in which each client type (mobile app, web app, partner API, smart-TV app, voice assistant) gets its own dedicated server-side aggregation and shaping layer that sits between the client and the underlying business services. The BFF aggregates calls to multiple internal services, transforms responses into the exact shape the client needs, applies client-specific authentication and rate-limiting policies, and absorbs the impedance mismatch between “what the business services produce” and “what this particular client wants to consume.” The pattern was named in Phil Calçado’s September 2015 SoundCloud engineering post (and Sam Newman’s near-simultaneous blog post elaborated the same pattern from the same SoundCloud experience), giving voice to a structure that had already evolved at Netflix as it expanded from a web-only service to mobile, smart TV, and partner-device clients each with radically different bandwidth, screen-size, and latency profiles. The BFF is structurally a particular kind of API Gateway System Design specialization — where a generic gateway handles cross-cutting concerns horizontally (one gateway, all clients), the BFF handles client-specific concerns vertically (one BFF per client type, each tuned to that client). This note covers the origin and motivation, structure with mermaid, comparison with API Gateway, real-world incarnations at Netflix and SoundCloud, the variants (per-client-type vs per-experience), and the failure modes — particularly the BFF-becomes-monolith trap and the team-ownership ambiguity that emerges as multiple BFFs accumulate similar logic.
1. When to Use / When Not to Use
The BFF emerges naturally as a system grows beyond a single client type and accumulates client-specific behavior. The decision to extract a BFF (rather than continue with a single shared API) is forced by either (a) divergent client needs that increasingly contort the shared API, or (b) divergent ownership that increasingly contorts cross-team coordination. In both cases the BFF buys client-team autonomy at the cost of an extra deployable component.
When BFFs fit
-
Multiple client types with substantially different needs. A mobile app on a 4G connection wants paginated, slim responses with pre-computed display strings; a web app on broadband wants richer responses with everything for client-side caching; a partner API wants stable, versioned, well-documented contracts; a smart-TV app wants pre-rendered image URLs sized for 1080p screens. A single shared API drifts toward the union of all needs and becomes both bloated and slow for every client.
-
Mobile-specific concerns. Mobile clients have asymmetric tradeoffs vs server: bandwidth is precious, battery is precious, latency is high, response shape matters. A mobile BFF can aggregate “give me the order detail screen” into one HTTP call that fans out to 5 internal services in fast intra-datacenter network — saving 4 round trips on the slow mobile network. This is the canonical mobile BFF use case.
-
Independent client-team autonomy. When the iOS team, the Android team, the web team, and the partner-integrations team all want to ship features at their own cadence without coordinating with a central API team, giving each their own BFF (which they own) collapses the coordination problem to “modify your own BFF + the underlying services.” Without BFFs, every client-driven change requires coordinating with the central API team.
-
Different security or compliance boundaries per client. Public-facing partner APIs need stricter rate limiting, contract stability, and compliance review; internal employee tools need single-sign-on integration; consumer mobile apps need OAuth 2.0 with mobile-specific flow. Conflating these in one API surface forces the strictest controls onto everyone.
-
Different observability needs. The mobile team cares about per-screen latency and crash rates; the partner team cares about contract violations and error budgets per partner; the web team cares about page-load metrics. A BFF per client type emits the right observability for that client without polluting other clients’ dashboards.
When BFFs do not fit
-
Single client type. If you have only a web app, a BFF is just a layer between the web app and the business services. That layer might still be useful for aggregation, but it doesn’t deserve the “BFF” framing because there’s no per-client-type variation. Just call it a gateway or a backend-for-the-app.
-
Small organization with one shared frontend team. If a single team owns iOS + Android + web and they coordinate naturally, multiple BFFs are operational overhead with no organizational benefit. A shared API or a single “client backend” suffices.
-
Heavy reliance on real-time bidirectional communication. WebSocket/streaming connections with stateful sessions (chat, multiplayer games) are awkward to put behind a BFF that’s optimized for stateless HTTP aggregation. The BFF can still handle authentication and routing for the connection, but the streaming logic itself often belongs in a dedicated streaming service.
-
When the underlying business services already match client needs. If the catalog service returns exactly what the mobile app needs in exactly the shape it needs, putting a BFF in front adds latency and operational cost with no aggregation or shaping value. The BFF is justified only when the business services don’t already match client needs.
-
When you would end up with one BFF per micro-feature. A common trap: every new feature gets its own BFF, leading to dozens of nearly-identical BFFs each owned by a different team. The pattern is one BFF per client type (or per experience), not per feature.
2. Structure
flowchart TB iOSApp[iOS App<br/>iPhone/iPad] AndroidApp[Android App<br/>Phone/Tablet] WebApp[Web SPA<br/>Browser] PartnerAPI[Partner / B2B<br/>Integration] TVApp[Smart-TV App<br/>Roku/Apple TV] iOSBFF[iOS BFF<br/><i>iOS-team owned</i><br/>aggregation + shape + iOS auth] AndroidBFF[Android BFF<br/><i>Android-team owned</i><br/>aggregation + shape + Android auth] WebBFF[Web BFF<br/><i>Web-team owned</i><br/>aggregation + shape + cookie auth] PartnerBFF[Partner BFF<br/><i>Platform-team owned</i><br/>versioned, rate-limited contracts] TVBFF[TV BFF<br/><i>TV-team owned</i><br/>image URLs sized for TV] iOSApp --> iOSBFF AndroidApp --> AndroidBFF WebApp --> WebBFF PartnerAPI --> PartnerBFF TVApp --> TVBFF subgraph Services[Internal Business Services] UserSvc[User Service] CatalogSvc[Catalog Service] OrderSvc[Order Service] InvSvc[Inventory Service] PaySvc[Payment Service] RecsSvc[Recommendations Service] SearchSvc[Search Service] NotifSvc[Notification Service] end iOSBFF --> UserSvc iOSBFF --> CatalogSvc iOSBFF --> OrderSvc iOSBFF --> RecsSvc AndroidBFF --> UserSvc AndroidBFF --> CatalogSvc AndroidBFF --> OrderSvc AndroidBFF --> RecsSvc WebBFF --> UserSvc WebBFF --> CatalogSvc WebBFF --> OrderSvc WebBFF --> InvSvc WebBFF --> PaySvc WebBFF --> SearchSvc PartnerBFF --> CatalogSvc PartnerBFF --> OrderSvc TVBFF --> CatalogSvc TVBFF --> RecsSvc
What this diagram shows. The vertical structure is the defining property of the BFF pattern. Each client type has its own dedicated BFF — iOS BFF, Android BFF, Web BFF, Partner BFF, TV BFF — owned by the team that owns the client. Below the BFFs, the same underlying business services (User, Catalog, Order, Inventory, Payment, Recommendations, Search, Notification) serve all BFFs uniformly. The BFFs are not identical: each fans out to a different subset of business services (the partner BFF doesn’t need recommendations; the TV BFF doesn’t need payment because TV apps don’t process payments) and shapes responses differently (iOS BFF returns slim, paginated responses; Web BFF returns richer responses with embedded related data; TV BFF returns pre-sized 1080p image URLs). The vertical “team-owns-its-stack” structure is exactly what makes BFFs valuable: the iOS team can deploy iOS BFF changes whenever they ship a new iOS app version, without coordinating with the Android team, the partner team, or the central API team. The separation between BFF and business services is what the BFF buys: the business services have a single, stable contract; each client team can change its BFF without touching the business services. Compare this to a single shared API serving all clients, which would require coordination on every change. The diagram intentionally omits a separate API gateway in front of the BFFs; in production, a thin gateway often sits in front of all BFFs to handle TLS termination, the very-coarsest-grained rate limiting, and DDoS protection — see §10 for the BFF-vs-Gateway comparison.
3. Core Principles
3.1 One BFF Per Client Type
The unit of decomposition is the client type (iOS, Android, Web, Partner, TV), not the feature and not the team (though team-ownership often aligns with client type). A common error is to create a BFF per micro-feature (“the Search BFF”, “the Cart BFF”) — this fragments the per-client logic across many components and reverses the pattern’s benefit, which is consolidating per-client concerns in one component the client team owns.
The reverse error — one BFF for “all mobile” combining iOS and Android — sometimes makes sense if iOS and Android genuinely have identical needs and a single team owns both. More often, iOS and Android have subtly different platform conventions (push notification handling, deep-link routing, biometric auth) and benefit from separate BFFs.
3.2 The BFF Is Owned by the Client Team
The team that ships the iOS app owns the iOS BFF. This is the organizational core of the pattern; without it, the structural separation buys nothing. If the iOS BFF is owned by a central platform team, every iOS feature still requires cross-team coordination — the BFF is just one more handoff. With client-team ownership, the iOS team can iterate on the BFF as fast as they iterate on the iOS app itself.
This ownership structure has implications: client teams must include backend engineering capacity, not just frontend specialists. In smaller organizations, this often means the client team is a vertical full-stack team rather than a frontend-only team. Newman 2021 emphasizes this point — the BFF pattern can fail if introduced into an organization where frontend and backend are strictly separated; it works best when teams own end-to-end capabilities.
3.3 The BFF Aggregates and Shapes — It Does Not Replace Business Logic
The BFF’s job is to make N service calls and combine their responses into the right shape for one client. It is not the place for business rules — those belong in the underlying business services. If the iOS BFF starts implementing pricing rules, the Android BFF implements them too (subtly differently), and the partner BFF implements them differently again, and now you have three implementations of pricing in three places. Pricing belongs in the Pricing service; the BFFs ask Pricing for the price.
The discipline here is: BFF code reads like “call A, call B, call C, combine, transform, return.” If it starts to look like “compute X, decide between Y and Z, recompute when condition holds,” that logic is misplaced. Move it into a business service.
3.4 The BFF Knows About Its Client; the Business Services Do Not
Business services are client-agnostic — they expose an API surface that any caller can use. The BFF translates between that generic surface and the client-specific surface. The Catalog service returns full product records; the mobile BFF translates that into a slim list-card record with a single thumbnail URL; the web BFF translates that into a rich detail record with a gallery of full-resolution images.
This translation layer is where client-specific knowledge lives — what fields a client needs, what shape, what computed display strings, what date format, what locale formatting. By centralizing this in the BFF, the business services remain reusable across all clients.
3.5 The BFF May Cache, but Carefully
A BFF is well-positioned to cache responses (it sees the request shape, knows the cache key, sees the client’s update frequency). However, BFF caching can mask bugs in underlying services and can serve stale data when business rules change. The general guidance: cache only in the BFF for read-heavy, latency-critical paths where the business service is the bottleneck; otherwise cache in the business service itself or in a dedicated cache layer.
4. Request Flow
sequenceDiagram participant Mobile as Mobile App participant BFF as Mobile BFF participant User as User Service participant Order as Order Service participant Item as Catalog Service participant Recs as Recommendations Service participant Cache as BFF Cache (Redis) Mobile->>BFF: GET /home-screen?user_id=42<br/>(single mobile-network round trip) BFF->>BFF: validate JWT, extract user identity BFF->>Cache: check cache key home:42 Cache-->>BFF: miss par Parallel fan-out — fast intra-DC network BFF->>User: GET /users/42 (~5ms) User-->>BFF: { name, prefs, ... } and BFF->>Order: GET /users/42/recent-orders?limit=3 (~10ms) Order-->>BFF: [ order1, order2, order3 ] and BFF->>Recs: GET /recommendations/42?context=home&limit=10 (~30ms) Recs-->>BFF: [ rec_id1, rec_id2, ..., rec_id10 ] end BFF->>Item: POST /items/batch { ids: [order1.items..., rec_ids...] } (~15ms) Item-->>BFF: [ item1, item2, ... ] (full records) BFF->>BFF: shape response for mobile:<br/>- slim each item to {id, name, thumbnail_url, price_str}<br/>- format prices in user's locale ("$24.99")<br/>- pick correct image size for screen DPI<br/>- compute display strings ("2 days ago") BFF->>Cache: SET home:42 (TTL 60s) BFF-->>Mobile: 200 { user, recent_orders, recommendations }<br/>(slim, mobile-shaped JSON)
The flow illustrates the BFF’s value proposition. The mobile client makes one network call over the slow, high-latency mobile network (typically 100-500ms round-trip on cellular). The BFF then makes 4 calls over the fast intra-datacenter network (each under 30ms). The total latency from the mobile client’s perspective is one slow round-trip plus the BFF’s aggregation time — let’s say 300ms. Without the BFF, the mobile client would make 4 calls itself, each at 300ms, for a total of 1.2 seconds (or ~600ms with parallel fetch and HTTP/2 multiplexing — still substantially worse). The fan-out from BFF to services happens in parallel where possible (the BFF code uses concurrent calls); the Item service call depends on the order and recommendations responses (it needs the IDs from both), so it happens after that join. The shaping step is BFF-internal: trim fields, format strings for the user’s locale, pick image sizes appropriate to the device. This shaping logic is mobile-specific and would not be reusable for the Web BFF, which would shape the same data differently.
The cache check at the start and cache write at the end are optional; they are useful for high-traffic screens where the same user might re-request the same screen many times (pull-to-refresh on mobile is a typical pattern). Cache TTL must balance freshness (orders should reflect new orders within seconds of placement) against backend load.
5. Variants
5.1 One BFF Per Client Type (Canonical)
The original Newman/Calçado pattern: one BFF for iOS, one for Android, one for Web, one for partner integrations. Each owned by the team that ships the corresponding client. This is the most common deployment.
5.2 One BFF Per “Experience”
A variant championed by some Netflix engineering posts: rather than dividing by client type (iOS vs Android), divide by experience — the “browse experience” gets one BFF used by all clients, the “playback experience” gets another. This makes sense when an experience cuts across clients with similar needs (browsing on iOS, Android, and Web is more similar to itself than browsing on iOS is similar to playback on iOS). It’s harder to align with team ownership, since usually teams are organized by client.
5.3 GraphQL as a BFF Replacement
A meaningful alternative: instead of one BFF per client, expose a single GraphQL endpoint and let each client query for exactly the fields it needs. The shaping that the BFF does explicitly becomes implicit in GraphQL’s query language. GitHub (since their 2017 GraphQL API), Shopify, and Spotify (their 2017 GraphQL adoption post) have all moved in this direction. The tradeoff: GraphQL replaces N BFFs with one schema, but the schema must satisfy all clients (similar problems to a single shared REST API), and operational concerns (query complexity limits, N+1 query patterns, caching) require careful work. Apollo Federation and similar tools allow GraphQL schemas to be composed from per-domain subgraphs, somewhat preserving the per-team autonomy benefit.
The pragmatic answer is often “BFFs and GraphQL coexist” — the BFF is the GraphQL server for that client, with a schema tuned to that client’s needs.
5.4 BFF as a Lambda / Serverless Function
For low-volume client surfaces (admin tools, internal dashboards), the BFF can be a serverless function rather than a long-running service. The cold-start latency is a real concern for user-facing clients but is acceptable for internal tools.
5.5 Edge BFFs
A BFF deployed at the edge (Cloudflare Workers, AWS Lambda@Edge, Fastly Compute@Edge) rather than in a central datacenter. Reduces latency for geographically distributed users by terminating the client connection close to the user. Tradeoff: the BFF’s calls to backend services now traverse the slow public internet from edge to origin, which may erase the latency saving for cache-miss requests.
5.6 Per-Tenant BFFs
A multi-tenant SaaS variant: each tenant (or each tenant tier) gets a BFF with tenant-specific behaviors. Rare; usually the tenant differentiation is data-level (different rows in the same database, different feature flags) rather than code-level (different BFF), because per-tenant code becomes operationally untenable past a few tenants.
6. Real-World Examples
SoundCloud (2014–2015). The BFF pattern was named in Phil Calçado’s 2015 SoundCloud blog post. SoundCloud’s experience: a single API serving the iOS, Android, and web clients had drifted toward the maximalist union of all clients’ needs, with each client paying for fields and aggregations it didn’t use. The migration was to extract one BFF per client; mobile network performance improved measurably (fewer round-trips, smaller payloads), and client-team velocity improved (each team could iterate without central API team coordination). Calçado’s post specifically credits the previous internal Twitter discussions and SoundCloud’s own iteration; the name “Backend for Frontend” was Calçado’s framing.
Netflix. Netflix has documented the pattern extensively in engineering blog posts (2013 onward). Their motivating problem was extreme: by 2013, Netflix supported ~1000 distinct device types, ranging from low-power smart TVs with very limited local computing power to high-end gaming consoles with rich local rendering. The single API approach was untenable; some devices needed pre-rendered HTML strings, others wanted raw JSON, others wanted optimized binary protocols. Netflix’s “Edge API” team became responsible for device-specific APIs — effectively per-class-of-device BFFs. Newman cites Netflix’s experience as a primary motivator for the BFF pattern’s articulation.
Spotify (2017). Spotify’s “GraphQL at Spotify” engineering post described their evolution from REST BFFs to a GraphQL gateway that effectively is the BFF — each client team’s needs are expressed in GraphQL queries against a federated schema rather than in a separate BFF service. The pattern’s substance (per-client shaping) is the same; the implementation differs.
Shopify. Shopify’s storefront and admin clients use distinct GraphQL APIs (the Storefront API and the Admin API), each tuned to the needs of its consumers. This is a coarse-grained BFF pattern at the API level rather than a per-platform-app level — one API serves all storefront clients (web, mobile, partner-built), another serves all admin clients. Shopify’s documentation explicitly references the BFF pattern as motivation for the API split.
GitHub. GitHub’s introduction of GraphQL (2017) replaced what had effectively been ad-hoc per-client REST adaptations. The internal motivation included BFF-shaped concerns: mobile clients needed different fields than the website needed, and the website needed different fields than partner integrations needed. GraphQL made it tractable to satisfy all without central API team coordination.
Microsoft Teams / Office 365 mobile clients. Microsoft has documented (Microsoft Learn architecture docs) that the Teams iOS, Android, and Web clients each have their own BFF in the Teams backend. The BFFs handle aggregation across the dozens of underlying services (chat, video, files, presence, calendar) and shape responses for the platform.
7. Tradeoffs
| Dimension | Pros | Cons |
|---|---|---|
| Client team velocity | Each client team owns its BFF; ships independently | Each client team needs backend engineering capacity |
| Mobile network optimization | One client request → one BFF response; backend fan-out happens fast | BFF must be co-located with backend services for the fast fan-out to work |
| Per-client shape | Each client gets exactly the data it needs | N BFFs to maintain, deploy, monitor |
| Per-client auth | Different OAuth flows for different clients (mobile, web, partner) | Authentication logic duplicated across BFFs (use shared library) |
| Per-client security/compliance | Partner BFF can have stricter contract stability than mobile BFF | Coordination needed when policies must be uniform |
| Cross-cutting concerns | Each BFF can have client-specific cross-cutting (logging, metrics tagged by client) | Cross-cutting concerns common to all clients are duplicated (resolved with shared libraries) |
| Independent deployability | Mobile BFF deploys when iOS app deploys, Android when Android, etc. | Coordination needed when underlying services break BFF-level contracts |
| Failure isolation | A buggy mobile BFF doesn’t break the web | Per-BFF reliability burden |
| Caching | Per-client cache shape | Risk of stale data leaks across clients |
| Operational footprint | Per-BFF logs, metrics, alerts | Operational sprawl as N grows |
| Code reuse | Can extract shared libraries for common patterns | Library evolution coordinates across BFFs (less than coordinating across teams without BFFs) |
| Schema evolution | Each BFF can version on its own | Changes to underlying services may require updating multiple BFFs |
8. Migration Path
From a single shared API to BFFs
The migration is incremental and low-risk; it’s one of the easier microservice patterns to adopt.
-
Identify the most-divergent client. The mobile client is usually the first candidate because mobile network constraints make aggregation valuable. Identify the most contorted endpoints in the shared API — those that have parameters like
?include=related,images,reviews&format=mobile&compact=true. Those endpoints are signaling that the shared API has been forced to accommodate the mobile client. -
Extract a Mobile BFF as a thin pass-through. Initially, the mobile BFF just calls the existing shared API and returns its response. Cut over the mobile client to the BFF endpoint via a feature flag.
-
Move client-specific logic into the BFF. As the mobile team needs new shapes or aggregations, they implement them in the BFF rather than in the shared API. The shared API stops accumulating mobile-specific endpoints.
-
Decommission mobile-specific endpoints from the shared API. Once mobile traffic has fully migrated, remove the mobile-specific endpoints; the shared API regains coherence.
-
Repeat for the next-most-divergent client. Web BFF, partner BFF, etc. Each migration is independent.
-
Eventually, the “shared API” becomes the partner BFF. If you extract every client into its own BFF, the original shared API may be unnecessary; what remains is just the business services. Or it may persist as the partner-facing BFF with formal contracts.
From BFFs back to a shared API
Sometimes the BFF pattern is over-applied and you want to consolidate. The reverse migration is:
- Identify BFFs with substantially-similar logic. Two BFFs (say, iOS and Android mobile) that 80% overlap.
- Extract the shared logic into a library or a shared “mobile experience” service.
- Reduce the BFFs to the truly client-specific 20%.
- Eventually merge them if the divergence shrinks below the cost of separate deployments.
Don’t merge prematurely — the BFF separation is buying real autonomy, and merging tightens the team coupling again.
9. Pitfalls and Anti-Uses
-
The BFF becomes a second monolith. Without discipline, BFFs accumulate business logic that doesn’t belong there — pricing rules, eligibility checks, tax calculations. Over time the BFF becomes a domain-rich monolith of its own, and the underlying services are reduced to dumb data retrieval. This reverses the microservices benefit. Discipline: BFF code looks like aggregation and shaping; if it starts to look like business decisions, move that logic into a business service.
-
Coordination cost when ALL BFFs need similar features. A change that needs to land in the iOS, Android, and Web BFFs (say, a new authentication scheme) requires coordinating across three teams. The BFF pattern intentionally trades centralized coordination for client autonomy, but the trade is bad when most changes are common across all clients. Mitigation: extract shared libraries (an “auth-helper” library used by all BFFs); accept that some changes will be slower than they were under a shared API.
-
Team ownership ambiguity. Who owns the iOS BFF? The iOS team. Who owns the partner BFF? Often unclear — the platform team, the API team, or the integrations team. If ownership is ambiguous, the BFF stagnates. The pattern requires explicit, named ownership for each BFF.
-
BFFs duplicating service-level logic. The mobile BFF rounds prices to two decimal places; the web BFF rounds prices to two decimal places; the partner BFF rounds prices to two decimal places. If the rounding rule changes (regulatory requirement to round up for fairness), three places must change. Mitigation: provide formatting utilities in shared libraries or in the underlying service’s response.
-
Tight coupling between BFF and underlying services. A BFF that knows about the User service’s internal field names is tightly coupled to that service. When the User service refactors its internal model, the BFF breaks. Mitigation: the User service’s external API surface is decoupled from its internal model; the BFF talks to the external API.
-
Cache coherence across BFFs. If the iOS BFF and Android BFF both cache the same underlying user record, an update needs to invalidate both caches. Cross-BFF cache invalidation is hard to get right. Mitigation: cache in the underlying service or via a shared cache layer rather than per-BFF when consistency matters.
-
Silent migration to client-specific infrastructure. “We need a special database for mobile-shaped queries.” This is a smell — the BFF should be using the same business services as everyone else. If the underlying services don’t support the queries the BFF needs, fix the services rather than building parallel infrastructure.
-
Confusion with API Gateway. The BFF and the API gateway are different patterns; conflating them leads to misshapen designs. The gateway is horizontal cross-cutting (auth, rate limiting, TLS, ingress); the BFF is vertical client-specific aggregation. They coexist: the gateway sits in front of the BFFs.
-
One BFF per micro-feature. “We need a Search BFF, a Cart BFF, a Checkout BFF, …” This fragments the per-client logic across many BFFs and reverses the pattern’s benefit. The discipline is one BFF per client type or per experience, not per feature.
-
No BFF when one is needed. The flip side: a system with five client types and a single shared API where every endpoint has special-case parameters for each client. The shared API has implicitly become a worse BFF — one with all the ownership confusion of a shared component and none of the autonomy benefit. Extract per-client BFFs.
10. Comparison With Sibling Architectures
vs API Gateway System Design
Both sit between clients and backend services. The differences:
| Dimension | API Gateway | BFF |
|---|---|---|
| Decomposition axis | Horizontal (cross-cutting concerns) | Vertical (per-client concerns) |
| Owned by | Platform / infra team | Client team |
| Responsibilities | Auth, rate limiting, TLS, routing, request size limits | Aggregation, shaping, per-client business workflow |
| Number per system | Usually 1 (or 1 per region) | One per client type |
| Where it sits | Frontmost — first thing the request hits | Behind the gateway, in front of business services |
| Logic tier | Should be thin and fast | Can be richer (aggregation, transformation, conditional fan-out) |
| Examples | Kong, Envoy, AWS API Gateway, Apigee | Custom per-team services (often Node.js, Spring Boot, Go) |
In production, a BFF typically sits behind a gateway. The gateway terminates TLS, validates the JWT, enforces global rate limits, and routes to the appropriate BFF; the BFF then handles the per-client aggregation. Confusing the two leads to designs that put either too much per-client logic in the gateway (bloating it and making it client-coupled) or too much cross-cutting concern in the BFF (duplicating it across BFFs).
vs Microservices Architecture
BFF is a pattern within microservices, not an alternative to it. The underlying business services are microservices; the BFFs are microservices specifically scoped to a client type. A microservices system without BFFs has its clients calling business services directly (or through a gateway) — viable for simple systems but contorts the API surface as client diversity grows.
vs Generic API Aggregator
A generic aggregator is a BFF without the per-client ownership — a single component that aggregates calls for all clients. This is closer to the shared API model and has the same drawback: it accumulates the union of all clients’ needs and becomes hard to evolve.
vs GraphQL
GraphQL replaces the explicit BFF with a single endpoint where each client queries for exactly the fields it needs. Substantively similar (per-client shaping); operationally different (one GraphQL server vs N BFFs). See §5.3 for the comparison.
vs Sidecar Pattern
Sidecar handles cross-cutting concerns next to a service (in the same pod). BFF is a separate component aggregating calls across services. The two patterns are complementary, not alternatives — a BFF can have a sidecar handling its mTLS, retries, etc., just like any other service.
vs Edge Services / Edge Workers
Edge services run in CDN POPs (Cloudflare Workers, Lambda@Edge) and are geographically close to users. A BFF can be deployed at the edge (a “Edge BFF”) for latency reasons. The pattern is the same; the deployment topology is different.
11. Common Interview Discussion Points
-
“What’s a BFF and when would you use one?” Per-client backend layer that aggregates and shapes. Use when client needs diverge enough that a single shared API becomes contorted, or when client-team autonomy matters more than central coordination.
-
“How does a BFF differ from an API Gateway?” Gateway = horizontal cross-cutting (auth, rate limit, TLS), one or few per system; BFF = vertical client-specific aggregation, one per client type. They coexist (gateway in front of BFFs).
-
“Who owns the BFF?” The client team. If the platform team owns it, the pattern’s autonomy benefit is lost.
-
“What if multiple BFFs need the same feature?” Extract shared libraries; coordinate the change across BFFs. Accept that some changes are slower — the pattern intentionally trades some coordination cost for autonomy.
-
“Should the BFF have business logic?” No — only aggregation and shaping. Business logic belongs in the underlying business services. Discipline test: BFF code reads as “call A, call B, combine, return.”
-
“How does the BFF handle authentication?” Different per client type. iOS uses OAuth 2.0 with PKCE; web uses cookie-based session; partner uses long-lived API tokens. The BFF translates client-specific auth to a uniform internal identity (typically a service-to-service JWT) before calling business services.
-
“What about caching in the BFF?” OK for read-heavy, latency-critical paths where cache coherence isn’t a concern. Risky for data that other clients might also be reading — cache in the business service or a shared cache layer instead.
-
“How do you migrate to BFFs?” Identify the most-divergent client (usually mobile); extract a thin pass-through BFF; move client-specific logic into it over time; remove client-specific endpoints from the shared API; repeat for the next client.
-
“What if you over-fragment into too many BFFs?” Consolidate by merging BFFs with substantially-similar logic, or by extracting shared logic into libraries. Don’t merge prematurely; the separation is buying real autonomy.
-
“GraphQL vs BFF?” GraphQL is one BFF per “GraphQL schema” — typically one endpoint serving all clients via per-client queries. Same substance (per-client shaping), different mechanism. Use GraphQL when the schema can serve all clients without contortion; use explicit BFFs when client divergence is severe enough that even GraphQL schemas would diverge.
12. See Also
- System Architectures MOC — parent MOC
- SWE Interview Preparation MOC — grandparent MOC
- Microservices Architecture — BFF is a microservices pattern
- API Gateway System Design — sibling pattern; horizontal vs vertical decomposition
- Service-Oriented Architecture — BFF addresses SOA’s “kitchen sink service” problem
- Sidecar Pattern — complementary cross-cutting pattern
- Anti-Corruption Layer Pattern — related translation pattern at a different boundary
- Strangler Fig Pattern — used when migrating to BFFs incrementally
- Service Mesh System Design — handles east-west cross-cutting that a BFF should not
- Distributed Tracing System Design — essential for debugging BFF aggregation
- Domain-Driven Design Strategic Patterns — bounded contexts within which BFFs operate
- Conway’s Law — organizational basis for client-team-owned BFFs
- Backend for Frontend Pattern — this note