BFF Layer for Frontend Developers: Why Your Project Might Need That Middleman

by JeariCk 6 min read
Building BFF using Node.js

What is the BFF

BFF stands for Backend For Frontend. It’s a pattern coined by Sam Newman around 2015, and the core idea is simple: instead of building one general-purpose backend API for all clients, create a dedicated backend service for each user experience.

The way I see it, it’s a translator sitting between the frontend and backend. It knows what your frontend looks like, what data it needs, and when — then fetches everything from the backend, assembles it, and hands it back clean.

Building BFF using Node.js
Building BFF using Node.js

Why the BFF even exists

Think about a typical scenario from a few years back: your company starts with a desktop web app. All backend APIs are designed with that one web client in mind. Then mobile arrives — iOS and Android apps need to be built.

Here come the problems. Mobile screens are smaller, they can’t display as much information as desktop. The data requirements are completely different. Plus mobile needs to worry about battery, bandwidth, and data caps — things the desktop never cared about.

The features themselves might differ too. On desktop you browse and order at your leisure. On mobile you might need barcode scanning for price comparison or in-store navigation. If the backend has just one general-purpose API, it either bloats up serving every client’s needs, or becomes a team bottleneck — every API change goes through a queue.

That’s when the BFF pattern saves the day.


Core principles

There’s a line in Sam Newman’s original article that I really like: “one experience, one BFF.” One user experience maps to one backend service built for that frontend.

E-commerce example:
– Desktop web gets its own BFF service
– iOS app gets its own
– Android app gets its own

Each dedicated backend serves only its corresponding frontend. Data format, endpoint count, aggregation logic — all driven by what that frontend actually needs. No more “why does the Android feature break iOS endpoints?”

SoundCloud took a slightly different approach: iOS and Android shared one mobile layer, with a separate one for desktop. The catch is, if iOS and Android diverge significantly in user experience, sharing creates friction.

The better rule is: **align these services with team boundaries.** If one team owns both iOS and Android, one shared service is fine. Two independent teams? Keep them separate.


What this layer actually does

1. Data aggregation

In a microservice architecture, a single page often pulls data from multiple services. A product detail page — product info from a product service, stock from inventory, pricing from a pricing service, recommendations from a recommendation service.

Without a middle layer, the frontend fires 4 requests and stitches them together. With it, one request goes out, the aggregation layer fetches all 4 downstream services in parallel, and returns the assembled result.

2. Data trimming

Same user info. On desktop list view you need name, email, avatar, role. On mobile list view you just need name and avatar. The BFF trims the response based on which client is asking.

3. Protocol translation

Your downstream might use gRPC, SOAP, or even old XML endpoints. This service translates it all into frontend-friendly JSON REST or GraphQL. The frontend never needs to know what’s happening on the other side.

4. Authentication and session management

It can also act as an auth gateway — handling login, token refresh, permission checks. The frontend gets clean, authenticated data.


BFF vs API Gateway

This is the most common question. Here’s the breakdown:

DimensionAPI GatewayBFF Layer
PurposeGeneral routing gatewayFrontend-specific backend
AudienceAll clientsSpecific frontend experience
ResponsibilitiesRouting, rate limiting, auth, loggingData aggregation, trimming, UI adaptation
Maintained byInfrastructure teamFrontend team
Code styleConfig-driven, genericTightly coupled to frontend

In production, they often work together: request hits Gateway (rate limiting, auth), then the BFF layer (data aggregation), then downstream microservices. The gateway doesn’t replace the frontend backend — they solve different problems.


Node.js in the picture

What language do frontend teams know best? JavaScript and TypeScript. Building this backend layer with Node.js means the frontend team owns it end to end. No cross-team communication overhead. That’s why so many teams choose Node.js for this role.

Popular choices:
Next.js API Routes / Server Actions — if you’re already on React, this is the most natural fit
NestJS — TypeScript full-stack framework for heavier setups
Express / Fastify — lightweight, roll your own

Quick Express example:

```javascript
 // compose product details for the frontend
 app.get('/api/product/:id', async (req, res) => {
 const [product, stock, price] = await Promise.all([
 fetch(`http://product-service/products/${req.params.id}`),
 fetch(`http://inventory-service/stock/${req.params.id}`),
 fetch(`http://pricing-service/price/${req.params.id}`),
 ]);

  const data = {

    ...(await product.json()),

    stock: (await stock.json()).quantity,

    price: (await price.json()).amount,

  };

  // Mobile doesn't need all fields

  if (req.headers['x-client'] === 'mobile') {

    delete data.description;

    delete data.specs;

  }

res.json(data);
 });
 ```


Common pitfalls

1. It becomes a “fat layer”

This service is supposed to be thin. If you cram business logic in there, it becomes another single point of failure. It should handle presentation-related logic and client adaptation — nothing more.

2. Added latency

Every extra hop costs time. If you don’t parallelize downstream calls, the serial overhead adds up fast. Always use Promise.all or equivalent for independent fetches.

3. Team ownership confusion

If the backend team maintains this layer, you’ve missed the point. The whole value here is that the frontend team owns it — releases at their pace, priorities driven by their UX. If changing a display field requires queuing with the backend team, there’s no benefit.

4. Over-engineering

A simple CRUD admin panel with one frontend client doesn’t need this. Adding a layer with zero benefit just creates maintenance overhead. It makes sense when you have multiple clients or distributed data sources — don’t jump on it for every project.


When to use this pattern

Based on Sam Newman’s original article and Microsoft’s Azure Architecture guide:

Good fit:
– Multiple frontend clients (Web, iOS, Android) with differing needs
– Microservice backend with distributed data sources
– Frontend team wants to own the API contract

Bad fit:
– Only one frontend client
– All clients have nearly identical requirements
– Project is still in MVP — speed trumps architectural purity


Wrapping up

This isn’t a flashy architectural pattern. It exists because of a real-world problem: in a multi-client world, general-purpose backends can’t keep up with every frontend’s unique requirements.

Its real value is moving API ownership from the backend to the frontend team. Building this kind of dedicated service isn’t about making frontend engineers write backend code — it’s about letting the people who know the user experience best decide how data is organized, assembled, and returned.

But it’s not free. An extra layer means an extra network hop, an extra service to deploy, an extra dimension of complexity. Whether you need it, and when, depends on your actual scenario — not on architectural trends.

Links:
Sam Newman — Backends For Frontends
Microsoft Azure — BFF Pattern

📖 Recommended Reading

Take a look at these articles, you may be interested

RSC(React Server Components) Guide — How They Work, Code Examples, and Framework Comparison

Introductory Analysis: Why Next.js 15 Became the Standard for Agentic AI Apps

Web Components + Astro + htmx: The 2026 Lightweight Frontend Trio

React Compiler 1.0 Is Here: Can We Finally Delete useMemo and useCallback?

DigitalOcean Referral Badge

This site is powered by DigitalOcean — reliable cloud hosting for developers.

Leave a Reply

Your email address will not be published. Required fields are marked *