Prompt
How do I build a Backend for Frontend?
Latest observation
A Backend for Frontend (BFF) is a thin backend service tailored to a specific client app, like a web app, mobile app, or partner portal. Its job is to hide backend complexity, aggregate data, and expose APIs shaped exactly for that frontend.
When to use a BFF
Use one when:
- You have multiple frontends with different needs
- Your frontend is making too many backend calls
- You need to compose data from several services
- You want client-specific auth, caching, or response shaping
- You want to protect internal services from direct frontend access
Core responsibilities
A BFF typically:
- Aggregates calls to multiple downstream services
- Transforms data into frontend-friendly shapes
- Handles auth/session/token exchange
- Implements request-specific caching
- Does lightweight validation and orchestration
- Hides internal service contracts from the client
It should not become a “mini monolith” containing major business logic.
How to build one
1. Define the frontend’s needs
Start with the client use cases:
- What screens need data?
- Which endpoints are called together?
- What data is over-fetched or under-fetched?
- What operations need orchestration?
Example:
- Home page needs user profile, notifications, and recommendations
- Checkout page needs cart, shipping options, and tax estimate
This tells you what endpoints your BFF should expose.
2. Design client-specific APIs
Expose endpoints that match frontend workflows, not backend service boundaries.
Instead of:
/users/123/orders?userId=123/recommendations?userId=123
Prefer:
/me/dashboard/me/checkout-summary/me/profile
These endpoints can combine multiple downstream requests into a single frontend call.
3. Keep business logic in downstream services
The BFF should orchestrate, not own core business rules.
Good BFF logic:
- merge data from services
- convert DTOs
- handle retries/timeouts
- choose response formats
Bad BFF logic:
- pricing rules
- eligibility calculations
- authorization policy decisions beyond request/session handling
- complex domain workflows
If logic starts growing, move it into a domain service.
4. Choose a tech stack
Pick something easy to build fast and integrate with your frontend ecosystem.
Common choices:
- Node.js / TypeScript: popular for web BFFs
- Go: strong for performance and simplicity
- Java / Spring Boot: good for enterprise environments
- .NET: common in Microsoft ecosystems
If your frontend team owns it, TypeScript is often a great default.
5. Implement the BFF layer
Typical components:
- Routing layer: defines client APIs
- Service clients: call downstream APIs
- DTO mappers: shape responses for the frontend
- Auth middleware: validates session/JWT
- Resilience: timeouts, retries, circuit breakers
- Caching: per-user or per-response where appropriate
- Logging/metrics/tracing: for observability
Example flow:
- Frontend calls
GET /me/dashboard - BFF validates user token
- BFF calls User Service, Notification Service, Recommendation Service
- BFF merges results into one response
- Frontend renders UI without multiple calls
6. Handle auth carefully
Common patterns:
- Cookie-based session for browser apps
- JWT passthrough to downstream services
- Token exchange if downstream APIs need different scopes
- BFF-managed sessions for improved security in browsers
For browser-based apps, a BFF can be a good way to avoid exposing tokens to the frontend JavaScript runtime.
7. Add resilience
Because the BFF depends on multiple services, it must fail gracefully:
- set short timeouts
- retry only safe operations
- use circuit breakers
- return partial results when acceptable
- degrade gracefully if one downstream service fails
Example:
- If recommendations fail, still return profile and notifications
8. Optimize for frontend performance
BFFs can dramatically improve UX by:
- reducing round trips
- compressing data into one response
- caching stable data
- prefetching or batching
But avoid making payloads too large. Return only what the screen needs.
9. Observe and measure
Add:
- structured logging
- request IDs / correlation IDs
- distributed tracing
- latency metrics per endpoint and downstream dependency
- error rate dashboards
You’ll want to know:
- which downstream service is slow
- whether the BFF is hiding service failures
- what endpoints are most expensive
Example architecture
Frontend (Web/Mobile)
|
v
BFF API
/ | \
User Orders Notifications Services
The frontend talks only to the BFF. The BFF talks to internal services.
Example endpoint
Suppose the frontend needs a dashboard:
Request
GET /api/me/dashboard
BFF does
- fetch user profile
- fetch unread notifications
- fetch recent orders
- fetch recommendations
Response
{
"user": {
"name": "Ava",
"tier": "Gold"
},
"notifications": [
{ "id": "n1", "text": "Your package shipped" }
],
"recentOrders": [
{ "id": "o1", "status": "Delivered" }
],
"recommendations": [
{ "id": "p1", "name": "Noise-canceling headphones" }
]
}
Common mistakes
- Putting all business logic in the BFF
- Sharing one BFF across very different clients
- Letting it become a second monolith
- Not setting timeouts and retries
- Exposing downstream service shapes directly
- Ignoring versioning and backward compatibility
- Forgetting observability
Good patterns
- One BFF per client type: web BFF, mobile BFF, partner BFF
- Thin orchestration layer
- Client-driven API design
- Strong contracts and testing
- Keep it stateless where possible
How to start small
- Pick one frontend and one high-value page
- Identify the calls it currently makes
- Build one BFF endpoint that replaces them
- Measure performance and developer experience
- Expand incrementally
Rule of thumb
If the frontend is making multiple calls just to render one screen, or if it needs client-specific API shaping, a BFF is a good fit.
If you want, I can also show:
- a Node.js/TypeScript BFF example
- a Spring Boot BFF example
- a reference architecture diagram
- or how to structure a BFF in a real project