About this article
As the sixth installment of the âFrontend Architectureâ category in the series âArchitecture Crash Course for the Generative-AI Era,â this article explains BFF.
A design pattern proposed by SoundCloud in 2011 as the answer to the problem of âno one is happy with a shared API.â This article covers the BFF background, typical responsibilities, implementation patterns (Next.js API Routes/tRPC/Hono), BFF vs GraphQL, antipatterns, and AI-era type sharing - presenting the operational iron rule of âkeep it thin, introduce it only when needed.â
What is BFF in the first place
BFF is, roughly speaking, âan intermediate layer that provides the optimal API per client.â
Imagine a hotel concierge. For business guests, they arrange meeting rooms and taxis; for tourists, maps and restaurant reservations â delivering only the info each guest needs in the optimal form. Handing everyone the same thick guidebook (a general-purpose API) leaves most pages unused. BFF plays this concierge role, shaping and returning only the data needed for each client â Web, mobile, TV, etc.
Why BFF is needed
Without BFF, one general-purpose API would serve all clients (Web, mobile, TV), but each client has completely different screen sizes, bandwidth, and data requirements. The result: returning the same response to everyone creates an API thatâs optimal for no one. The frontend discards unneeded data and supplements missing data with extra requests â inefficiency compounds, degrading both performance and security.
âShared APIâ makes nobody happy
It used to be common to share âone general-purpose APIâ across all clients, but Web/mobile/TV each have completely different screen sizes, required data, and auth requirements, leading to the problem of âno one is happy with a shared API.â BFF solves this through âan intermediate layer that provides the optimal API per client.â
The BFF philosophy is to separate responsibilities per client.
How BFF was born
The BFF pattern was proposed around 2015 by SoundCloud engineers, then spread through adoption in mobile apps at Netflix, Spotify, Twitter, and others. The background is that âas a result of microservices proliferation,â frontends came to need to call dozens of different APIs.
Old: one general-purpose API shared by all clients
[Web] âââ
[Mobile]ââ [single API] âââ [Backend]
[TV] âââ
The problems with this structure are three: âWeb changes affect Mobile,â âeach client fetches fields it doesnât use,â and âauth requirements per client get mixed in.â Web and mobile also have different bandwidth constraints, so a 1-API share was recognized as unrealistic - and BFF was born.
Typical BFF responsibilities
The BFF ideal is âa thin server doing front-endâs grunt work.â Business logic stays in the backend services, and BFF dedicates itself to display, formatting, and proxying - thatâs the healthy design.
| Responsibility | Content |
|---|---|
| API aggregation | Gather from multiple microservices into one screenâs worth |
| Data shaping | Convert to a screen-friendly format, drop unneeded fields |
| Auth/session | Token management, cookie-ization, hiding secret keys |
| Cache | HTTP cache for API responses, edge cache |
| Rate limit | DDoS countermeasures, API abuse prevention |
In particular, API aggregation matters - when displaying one screen requires separately calling âuser info, order history, recommendations, ads,â BFF can âparallel callâ these behind the scenes and bundle them into one response. The browser doesnât have to make 5 requests, and felt speed improves dramatically.
Typical BFF composition
The most common composition uses a full-stack FW like Next.js as the BFF layer. By placing it in the same repo as the frontend, âthe same team handles UI development and API shaping.â
[Browser]
â (same-origin communication, Cookie)
[Next.js API Routes / Server Actions] â BFF layer
â (mTLS / internal network)
[Microservices / external APIs]
The BFF owner is the frontend team, and placing it in the same repo as the frontend is the rule. Since BFF changes alongside screen changes, separate repos balloon the change cost. Living together means âchange a screen and PR the BFF in the same flow.â
Why BFF is effective
Inserting a BFF gives three concrete effects, large in scope. These canât be obtained by simply hitting APIs directly.
1. Improved security
Hitting external APIs directly from the browser exposes API keys and auth tokens browser-side. With BFF in the middle, tokens stay only on the server side, and only an httpOnly Cookie is handed to the browser. The risk of tokens being stolen via XSS (Cross-Site Scripting, a script injection attack) is fundamentally eliminated.
2. Improved performance
You can âparallelize the calls to multiple microservices on the BFFâ and bundle them into a single response. Browser-to-BFF is fast (same-origin), BFF-to-backend is fast (internal network) - âdouble optimizationâ applies.
3. Frontend dev efficiency
Because the frontend team can âdecide the API shape themselves,â they can cleanly format only the data needed for the screen. The back-and-forth of asking the backend team to âadd this fieldâ disappears.
BFF implementation patterns
BFF implementation methods are varied. Framework-built-in is easy; type-safe options like tRPC are popular as the edgy choice.
| Pattern | Notes |
|---|---|
| Next.js API Routes / Server Actions | Most prevalent in the React family |
| Nuxt Server Routes | Equivalent option in the Vue family |
| Remix Loader/Action | Web-standards-focused design philosophy |
| tRPC | Godlike dev experience via TypeScript type sharing |
| Hono + Cloudflare Workers | Edge BFF, low latency |
| Dedicated Express/Fastify | Lightweight, repo-separate from FE |
With the arrival of Next.js Server Components and Server Actions, the need to âbuild a separate BFFâ has diminished, and BFF becoming a built-in framework concern is becoming the mainstream. For simple use cases, Next.js alone is sufficient.
Type-safe BFF with tRPC
tRPC is a mechanism for fully sharing TypeScript types between server and client - rapidly spreading in recent years. Without schema definitions like REST or GraphQL, you can write APIs as if calling functions.
// server side
const appRouter = router({
user: {
byId: procedure.input(z.string()).query(({ input }) => ...)
}
})
// client side - server's types apply automatically
const user = await trpc.user.byId.query("u_1")
// user's type is auto-inferred, input validated by Zod
Benefits: No schema files, editor completion via types, runtime validation (integration with Zod (a TypeScript-only schema definition library)). On TypeScript full-stack projects (FE/BE coexisting in one project), it shows overwhelming dev efficiency.
Next.js + tRPC + Zod is the strongest modern type-safe full-stack composition.
BFF vs GraphQL
GraphQL is a technology solving similar problems to BFF, and the choice between them is often confusing. The two are ânot exclusiveâ - they can be combined - but their strengths differ, so use them for the right scenarios.
| Viewpoint | BFF | GraphQL |
|---|---|---|
| Implementation | Endpoints per screen | Single endpoint |
| Learning cost | Light | Heavy (design/operation hard) |
| Flexibility | Server-side fixed shaping | Client queries freely |
| Cache | Easy via HTTP standards | Requires separate design |
âWhen BFF fitsâ: few clients (Web/mobile), screen design is stable. âWhen GraphQL fitsâ: diverse clients (mobile/TV/external partners) want to query freely.
For single-client-centric use, BFF; for diverse clients querying freely, GraphQL is the rule.
Session-management standard
For auth when using BFF, the standard is âonly Cookie to the browser, real tokens managed behind the BFF.â This composition maximizes both XSS resistance and token management.
1. Browser â IdP (Identity Provider, the auth foundation, e.g., Auth0) gets ID token
2. ID token sent to BFF
3. BFF verifies, issues httpOnly Session Cookie
4. Subsequent API calls are Cookie-based (browser side never touches JWT)
5. BFF manages Access Token / Refresh Token behind the scenes
The decisive benefit of this composition is never exposing JWTs to the browser. The chance of Session Cookie being stolen via XSS is eliminated by httpOnly, and even if stolen, the server can revoke immediately. Compared to designs that put JWTs in the browser, security reaches one rank higher.
BFF + httpOnly Cookie is the safest composition for modern Web auth.
BFF antipatterns
BFF is convenient, but if you donât keep it appropriately thin, it bloats rapidly into new debt. Without grasping these typical failure patterns, the BFF itself becomes another monolith.
- Bloating: business logic flows into the BFF and duplicates with core services. Fixing requires changing both
- Siloization: each BFF starts its own user management or cache, and consistency is lost
- Cache placement problem: confusion about where (in BFF / in backend / on CDN) and how long to cache
- Auth duplication: each BFF independently implements auth, and auth bugs sprout everywhere
BFF is a thin layer for the frontend. Bringing in business logic duplicates with core services and breaks down.
âThe day BFF became the main systemâ (industry stories)
Thereâs a story told at study groups about a site that piled discount calculations and inventory reservations into Next.js API Routes âbecause the backend team was slow to respondâ - resulting in a reverse structure a year later: the BFF effectively became the main system, and the real Backend became a CRUD storage. Fixing required changing both, and they exhausted themselves on dual maintenance.
The scene of âletâs just write it quickly in Next.jsâ producing a horrifying reversal six months later is witnessed at many sites. The scary part of this accident: at the individual PR level, it was just âwriting a littleâ. A one-line discount calculation became inventory reservation in a month, point calculation in six months, and âmost of the core logicâ in a year - the typical pattern.
BFF âlooks harmless at the entrance, but always bloats if left alone.â The lesson âdonât put a vault in the entrance lobbyâ was born from accumulating these accidents.
The iron rule for BFF is keep it thin. Pound on the early signs of business logic creeping in.
Phased BFF adoption decision matrix
The most important judgment for BFF is whether to introduce it, and it auto-derives from scale and architecture. Hereâs the practical phased table.
| Phase | Scale/architecture | BFF treatment | Implementation method |
|---|---|---|---|
| 1. Personal/MVP | ~1,000 MAU | Unneeded (direct API calls) | fetch + useState |
| 2. Early startup | ~10,000 MAU, single backend | Thin BFF (FW built-in) | Next.js API Routes / Server Actions |
| 3. Mid-size SaaS | 10,000-100,000 MAU, multiple microservices | Aggregation BFF | tRPC + Next.js |
| 4. Large | 100,000+ MAU, multi-team | Dedicated BFF layer (per screen) | Dedicated Express/Fastify + gRPC |
| 5. Multi-client | Web + Mobile + TV | Per-client BFF | Each client dedicated |
| 6. Edge-distribution priority | Global, low-latency-focused | Edge BFF | Hono + Cloudflare Workers |
âThe substantive lower bound for adopting BFF is when microservices + aggregation across 3+ services is required.â Below that, the thin layer of Next.js API Routes / Server Actions is enough; spinning up a dedicated BFF server is over-design.
BFF goes in when needed. Adopting âfor the futureâ backfires on operational costs.
BFF designâs pitfalls and forbidden moves
Here are the typical accidents in BFF operation. All of them are entry points to BFF becoming the main system.
| Forbidden move | Why itâs bad |
|---|---|
| Write business logic in BFF | Duplicates with core service in a year. Donât put a vault in the entrance lobby |
| BFF owned by the backend team | Every screen change requires cross-team coordination. Frontend team ownership is the rule |
| Separate repos for BFF and FE | Screen change requires 2 PRs, accidents at sync timing |
| No cache strategy in BFF | Origin load concentrates, BFF becomes the bottleneck |
| Re-implementing auth in BFF (each BFF independently) | Auth bugs sprout everywhere. Lean on unified auth |
| Connect BFF and FE with REST + hand-typed types | Types are split, dual-managed type definitions. Fully share via tRPC |
| No timeout in BFF | Backend failures topple the BFF, avalanche cascade |
| Update without optimistic locking in BFF | Concurrent updates cause lost updates, data corruption |
| Letting non-frontend clients hit the BFF too | The point of BFF disappears. Maintain use-case specialization |
| Unify multiple microfrontends on one BFF | Team independence is lost. One BFF per frontend |
| Adopting BFF early as âmore modern even at small scaleâ | Over-design. Server Actions are enough at that scale â dedicated BFF just adds ops burden |
| Confusing âBFF and GraphQL are the sameâ | Opposite philosophies. BFF for few clients, GraphQL for many clients with free queries |
âAs of 2024 the standard is Next.js Server Actions + tRPC.â Fetch with Server Components, update with Server Actions, share types with tRPC. With this triad, âBFF as a separate layerâ can be implemented without consciousness of it. Traditional separate-server BFFs are considered only when there are 5+ microservices and multi-team parallel development.
BFF is the entrance lobby. The moment you bring in business logic, it becomes a second monolith.
AI decision axes
| Favored in the AI era | Disfavored in the AI era |
|---|---|
| tRPC (full type sharing) | Hand-written REST + dual-managed type definitions |
| Next.js Server Actions (self-contained in one file) | BFF in a separate repo, scattered specs |
| Validation via Zod schemas | Hand-written string verification |
| Same repo with FE/BE coexisting | Separated repos, distant specs |
- Judge necessity from scale (small scale: not needed; microservices: required)
- Prefer framework built-ins (Next.js API Routes / Server Actions)
- Type sharing + same repo (tRPC + Zod is strongest)
- Keep BFF thin (business logic stays in core services)
tRPC + Zod combination maximizes AI generation accuracy
tRPC automatically shares types between frontend and backend, and when validation is defined with Zod schemas, API input/output types automatically propagate to the frontend. For AI, âthe list of available APIs and each parameterâs typesâ are explicit in the codebase, so API-call generation accuracy rises dramatically.
With hand-written REST APIs + dual-maintained type definitions, when frontend types and server implementation diverge, AI canât judge which to trust and risks generating inaccurate code.
What to decide - what is your projectâs answer?
For each of the following, try to articulate your projectâs answer in 1-2 sentences. Starting work with these vague always invites later questions like âwhy did we decide this again?â
- Whether to set up a BFF (judge from scale and architecture)
- Implementation tech (Next.js / tRPC / Hono / dedicated server)
- Repo composition (coexist with FE / separate repo)
- How to handle auth (Cookie / Bearer / Session)
- Cache strategy (in BFF / CDN / which has priority)
- Logging and monitoring (correlation IDs, OpenTelemetry)
Related Articles
https://en.senkohome.com/arch-intro-frontend-overview/ https://en.senkohome.com/arch-intro-frontend-framework/ https://en.senkohome.com/arch-intro-index-frontend/
Summary
This article covered BFF, including responsibilities, implementation patterns, differences with GraphQL, antipatterns, and type sharing.
Keep BFF thin, judge necessity from scale, and maximize AI productivity through type sharing + same repo. That is the practical answer for BFF design in 2026.
Next time weâll cover authentication and authorization (frontend side) (Cookie/JWT storage, XSS/CSRF countermeasures).
I hope youâll read the next article as well.
đ Series: Architecture Crash Course for the Generative-AI Era (36/89)