
When building a modern web application, the frontend often needs to communicate with multiple backend services:
Browser
│
├── Auth Service
├── Product Service
├── Order Service
└── Notification Service
This works, but it makes the frontend tightly coupled to the backend architecture.
A better approach in some systems is to introduce a Backend for Frontend (BFF).
Browser
│
▼
Next.js BFF
│
├── Auth Service
├── Product Service
├── Order Service
└── Notification Service
What is a BFF?
A BFF is a backend layer specifically designed for a particular frontend.
Instead of exposing all backend services directly to the browser, the frontend communicates with the BFF, and the BFF communicates with the internal services.
This gives us a place to handle:
- Authentication
- Authorization
- Request validation
- API aggregation
- Response transformation
- Caching
- Hiding internal service URLs
Next.js as a BFF
Next.js can implement a BFF using Route Handlers.
For example:
app/
└── api/
└── products/
└── route.ts
// app/api/products/route.ts
export async function GET() {
const response = await fetch(
`${process.env.PRODUCT_SERVICE_URL}/products`
);
if (!response.ok) {
return Response.json(
{ message: "Failed to fetch products" },
{ status: 500 }
);
}
const products = await response.json();
return Response.json(products);
}
Now the browser calls:
GET /api/products
Instead of directly calling:
GET http://product-service:3002/products
The architecture becomes:
Browser
│
│ GET /api/products
▼
Next.js BFF
│
│ GET /products
▼
Product Service
The internal service URL remains server-side.
API Aggregation
One of the most useful BFF features is aggregation.
Imagine a dashboard needs data from four different services:
User Service
Order Service
Notification Service
Recommendation Service
Instead of making four requests from the browser:
Browser
├── GET /users/me
├── GET /orders
├── GET /notifications
└── GET /recommendations
The BFF can expose a single endpoint:
GET /api/dashboard
And fetch the data in parallel:
// app/api/dashboard/route.ts
export async function GET() {
const [
user,
orders,
notifications,
recommendations,
] = await Promise.all([
fetch(`${process.env.USER_SERVICE_URL}/me`)
.then(res => res.json()),
fetch(`${process.env.ORDER_SERVICE_URL}/orders`)
.then(res => res.json()),
fetch(`${process.env.NOTIFICATION_SERVICE_URL}/notifications`)
.then(res => res.json()),
fetch(`${process.env.RECOMMENDATION_SERVICE_URL}/recommendations`)
.then(res => res.json()),
]);
return Response.json({
user,
orders,
notifications,
recommendations,
});
}
Now the browser only needs:
GET /api/dashboard
The BFF handles the complexity behind the scenes.
Response Transformation
The BFF can also transform backend responses into a format that is better suited for the frontend.
For example, a backend might return:
{
"id": 15,
"first_name": "Abanoub",
"last_name": "Kerols",
"internal_role_id": 7
}
But the frontend may only need:
{
"id": 15,
"name": "Abanoub Kerols"
}
The BFF can perform this transformation:
return Response.json({
id: user.id,
name: `${user.first_name} ${user.last_name}`,
});
This prevents the frontend from becoming dependent on internal backend models.
Authentication
A BFF can also be useful for authentication.
For example:
Browser
│
│ HttpOnly Cookie
▼
Next.js BFF
│
│ Authorization: Bearer <token>
▼
Backend Service
The BFF can read the server-side authentication state and attach the appropriate credentials when calling internal services.
This can help keep sensitive tokens away from client-side JavaScript.
The Important Architectural Boundary
Using Next.js as a BFF does not mean moving all business logic into Next.js.
A good separation is:
Next.js
┌───────────────┐
│ UI │
│ BFF │
│ Authentication│
│ Aggregation │
│ Transformation│
└───────┬───────┘
│
▼
Backend Services
┌───────────────┐
│ Business Logic│
│ Domain Rules │
│ Data Access │
└───────────────┘
The BFF should mainly adapt backend capabilities to the needs of the frontend, while important domain logic remains in the backend services.
Final Architecture
Browser
│
│ HTTPS
▼
┌─────────────────┐
│ Next.js │
│ BFF │
│ │
│ Auth │
│ Validation │
│ Aggregation │
│ Transformation │
│ Caching │
└────────┬────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
Auth API Product API Order API
│ │ │
▼ ▼ ▼
Database Database Database
The key idea
Next.js doesn't have to replace your backend. It can become the backend layer specifically designed for your frontend.
That's the core idea behind using Next.js as a Backend for Frontend (BFF).
Top comments (0)