The Problem That Started It All
Picture this. Your team has just finished breaking a monolithic application into 15 independent microservices. Everyone is happy. Then the mobile team comes to you and says:
"Our app is calling six different services just to render the home screen, and half the data we get back we don't even need."
A week later, the web team raises a similar concern. Their page requires slightly different data, structured differently, with a few additional fields that the mobile app doesn't care about.
Now you're faced with difficult choices:
Should you modify the microservice APIs to satisfy the mobile team? That could break the web application.
Should you add optional fields everywhere? That quickly fills your services with client-specific logic unrelated to their business responsibilities.
This exact challenge—first experienced at scale by streaming and e-commerce companies supporting many different client applications—led to the creation of the Backend for Frontend (BFF) architectural pattern.
So, What Exactly Is a BFF?
In simple terms, a Backend for Frontend (BFF) is a dedicated backend layer built specifically for a single frontend application or client type.
Instead of every client—such as a web application, mobile app, smart TV app, or third-party integration—communicating directly with shared microservices, each client communicates with its own backend layer.
That backend has one primary responsibility:
Call the required microservices.
Aggregate and reshape the returned data.
Return exactly the data that particular frontend needs, in the format it expects.
Think of a BFF as a personal translator standing between your frontend and your microservices. The frontend doesn't need to understand the complexity of multiple backend services—it simply communicates with its dedicated translator, which knows exactly what that frontend expects.
Web App Mobile App Smart TV App
| | |
Web BFF Mobile BFF TV BFF
| | |
\________________|_________________/
|
┌───────────────┼────────────────┐
Orders Service Catalog Service User Service
Notice something important.
There is still only one set of core microservices.
The BFF does not replace them. It simply sits in front of them as a client-specific layer.
A General Example
Imagine an e-commerce platform.
Different clients require different data.
Mobile App
The mobile application only needs:
Product name
Price
Thumbnail image
Star rating
The payload should remain small because:
Mobile networks may be slower.
Screen space is limited.
Web Application
The web application requires richer information, including:
Product name
Price
Full image gallery
Detailed specifications
Related products
Customer reviews
Desktop users generally expect a more detailed experience.
Admin Dashboard
An internal admin portal requires completely different information:
Product name
Price
Stock count
Supplier information
Audit logs
Most of this data should never be exposed to customers.
Without a BFF, a shared API typically ends up doing one of two things:
Returning everything to everyone, creating unnecessarily large payloads.
Filling business services with conditional logic such as:
if (clientType == "mobile")
{
...
}
Now your Product Service knows about mobile apps, web apps, and admin dashboards—responsibilities it should never have.
With the BFF pattern, you instead build three lightweight layers:
Mobile BFF
Web BFF
Admin BFF
Each one calls the same Product Service, Inventory Service, and Review Service, but shapes the response differently for its own client.
A Simple C# Example
The following ASP.NET Core Minimal API demonstrates a Mobile BFF that aggregates data from two microservices into a lightweight response.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("ProductService", c =>
c.BaseAddress = new Uri("https://catalog-service/"));
builder.Services.AddHttpClient("ReviewService", c =>
c.BaseAddress = new Uri("https://review-service/"));
var app = builder.Build();
app.MapGet("/mobile/products/{id}", async (string id, IHttpClientFactory factory) =>
{
var productClient = factory.CreateClient("ProductService");
var reviewClient = factory.CreateClient("ReviewService");
var productTask = productClient.GetFromJsonAsync<ProductDto>($"products/{id}");
var reviewTask = reviewClient.GetFromJsonAsync<ReviewSummaryDto>($"reviews/{id}/summary");
await Task.WhenAll(productTask, reviewTask);
// Shape the response exactly how the mobile app wants it.
var response = new
{
productTask.Result!.Name,
productTask.Result.Price,
Thumbnail = productTask.Result.Images.First(),
Rating = reviewTask.Result!.AverageRating
};
return Results.Ok(response);
});
app.Run();
Notice what this API does not contain:
No business logic
No database
No domain rules
Its responsibility is simply:
Calling services
Aggregating responses
Returning data in the exact format required by the mobile application
That is the essence of the BFF pattern.

Join the conversation! Your thoughts help the community grow.