DEV Community

Cover image for Using Next.js as a Backend for Frontend (BFF)
Abanoub Kerols
Abanoub Kerols

Posted on

Using Next.js as a Backend for Frontend (BFF)


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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
// 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);
}
Enter fullscreen mode Exit fullscreen mode

Now the browser calls:

GET /api/products
Enter fullscreen mode Exit fullscreen mode

Instead of directly calling:

GET http://product-service:3002/products
Enter fullscreen mode Exit fullscreen mode

The architecture becomes:

Browser
   │
   │ GET /api/products
   ▼
Next.js BFF
   │
   │ GET /products
   ▼
Product Service
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Instead of making four requests from the browser:

Browser
 ├── GET /users/me
 ├── GET /orders
 ├── GET /notifications
 └── GET /recommendations
Enter fullscreen mode Exit fullscreen mode

The BFF can expose a single endpoint:

GET /api/dashboard
Enter fullscreen mode Exit fullscreen mode

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,
  });
}
Enter fullscreen mode Exit fullscreen mode

Now the browser only needs:

GET /api/dashboard
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

But the frontend may only need:

{
  "id": 15,
  "name": "Abanoub Kerols"
}
Enter fullscreen mode Exit fullscreen mode

The BFF can perform this transformation:

return Response.json({
  id: user.id,
  name: `${user.first_name} ${user.last_name}`,
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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   │
        └───────────────┘
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)