Backend for Frontend (BFF)
How to build specialized backends per client type to optimize the experience of each platform without coupling the internal services.
What problem it solves
In a microservices architecture with a single API Gateway, every client (web, mobile, IoT, smart TV) consumes the same API. But each client has very different needs:
- Mobile: Needs compact responses (limited bandwidth), aggregated data in few calls
- Web SPA: Can handle more data, needs pagination and advanced filters
- IoT: Only needs a minimal subset of data with lightweight protocols
Problema con un solo API Gateway:
Mobile App ──► API Gateway ──► Microservicios
Web App ──► (misma API)
IoT Device ──► (misma API)
La API devuelve TODOS los campos para TODOS los clientes
Mobile recibe datos que no necesita → desperdicio de ancho de banda
Web no puede pedir datos extra sin afectar a mobile
Cada cambio en la API afecta a todos los clientes
The result is a “one-size-fits-all” API that serves no client well, or an API full of conditional parameters (?platform=mobile&fields=name,price) that becomes hard to maintain.
How it works
The BFF pattern proposes creating a dedicated backend for each type of client. Each BFF is a lightweight service that acts as an intermediary between its specific client and the internal microservices.
Con BFF:
Mobile App ──► BFF Mobile ──┐
Web App ──► BFF Web ──┤──► Microservicios
IoT Device ──► BFF IoT ──┘
Cada BFF adapta las respuestas a su cliente
Cada equipo de frontend controla su BFF
Los microservicios no cambian
BFF responsibilities
| Responsibility | Description | Example |
|---|---|---|
| Aggregation | Combine data from multiple services | Mobile dashboard with 3 services in 1 call |
| Transformation | Adapt data format and structure | Trim fields for mobile, expand for web |
| Orchestration | Coordinate service calls in order | Login → profile → preferences in sequence |
| Client-specific caching | Cache according to client patterns | Mobile caches catalog for 10 min, web for 2 min |
| Authentication | Handle client-specific auth | OAuth for web, API key for IoT |
Example: product endpoint
Microservicio Product Service devuelve:
{
"id": "P001",
"name": "Laptop Pro",
"description": "Laptop de alto rendimiento...",
"specs": { "cpu": "i9", "ram": "32GB", "storage": "1TB" },
"images": ["url1.jpg", "url2.jpg", "url3.jpg", "url4.jpg"],
"reviews": [...50 reviews...],
"relatedProducts": [...10 productos...],
"inventory": { "warehouse1": 50, "warehouse2": 30 },
"pricing": { "base": 1500, "discount": 10, "tax": 15 }
}
BFF Mobile devuelve:
{
"id": "P001",
"name": "Laptop Pro",
"image": "url1_thumb.jpg",
"price": "$1,350",
"rating": 4.5
}
BFF Web devuelve:
{
"id": "P001",
"name": "Laptop Pro",
"description": "Laptop de alto rendimiento...",
"specs": { "cpu": "i9", "ram": "32GB", "storage": "1TB" },
"images": ["url1.jpg", "url2.jpg", "url3.jpg", "url4.jpg"],
"reviewSummary": { "average": 4.5, "count": 50 },
"price": { "original": 1500, "final": 1350, "discount": "10%" },
"relatedProducts": [...3 productos...]
}
BFF ownership
A key principle of the pattern is that the frontend team owns its BFF. This enables:
- The mobile team decides what data it needs and how to structure it
- Changes to the UI don’t require coordinating with other teams
- Each BFF evolves at the pace of its client
Advantages
- Per-client optimization: Each platform receives exactly the data it needs
- Team autonomy: The frontend team controls its backend without depending on others
- Independent evolution: Changes to the mobile API don’t affect web or IoT
- Performance: Less data transferred, fewer calls from the client
- Client simplicity: The frontend consumes an API designed specifically for it
- Granular security: Each BFF can implement client-specific security policies
Trade-offs / Drawbacks
- Code duplication: Similar logic may be repeated across BFFs (mitigable with shared libraries)
- More services to maintain: Each BFF is an additional service that needs deployment and monitoring
- Consistency: Different BFFs may return inconsistent data if not coordinated
- Organizational complexity: Requires teams with full-stack capability (frontend + its BFF)
- Latency: An extra hop between the client and the microservices
- Risk of a monolithic BFF: If the BFF grows too large, it turns into a mini-monolith
When to use it
- Multiple types of client with very different data needs
- Frontend teams that need autonomy to evolve their API
- Mobile applications where bandwidth and the number of calls matter
- When a single API Gateway becomes too complex with per-client conditional logic
- Organizations with dedicated teams per platform (mobile team, web team)
When to avoid it
- A single type of client (web only, or mobile only)
- Clients with very similar data needs
- Small teams where maintaining multiple BFFs is not justified
- When an API Gateway with simple transformations is enough
- Prototypes or MVPs where development speed is the priority
Common technologies and implementations
| Category | Options |
|---|---|
| Frameworks | Express/Fastify (Node.js), Spring Boot (Java), ASP.NET Core (.NET) |
| GraphQL as BFF | Apollo Server, Hasura, Strawberry (Python) |
| API Gateway + BFF | Kong + BFF services, AWS API Gateway + Lambda |
| Internal communication | gRPC between BFF and microservices, REST, messaging |
Relationship with other patterns
- API Gateway: The BFF is a specialization of the API Gateway per client type
- Microservices: BFFs consume internal microservices and adapt them
- Anti-Corruption Layer: The BFF can act as an ACL between the frontend and legacy services
- Circuit Breaker: Each BFF should implement circuit breaking toward the services it consumes
Next steps
The BFF gives you granular control over each client’s experience. To understand how to decompose the services the BFF consumes, explore the Microservices pattern. To protect communication between the BFF and external services, review the Anti-Corruption Layer.