Prompt
What are Backend for Frontend best practices?
Latest observation
Backend for Frontend (BFF) best practices are mostly about making each frontend experience simple, fast, and independently evolvable while keeping the backend maintainable.
Core principles
1. Build a BFF per client type, not one “universal” BFF
Have separate BFFs for different frontends when their needs differ:
- Web app
- Mobile app
- Partner/admin portal
- TV/IoT
Why:
- Each frontend has different latency, payload, and auth needs
- Avoids bloated “one API for everything” designs
- Lets teams release independently
2. Keep the BFF thin
The BFF should:
- Aggregate and shape data
- Handle presentation-specific logic
- Hide backend complexity from the client
It should not become:
- A full business logic layer
- A place to duplicate domain rules
- A second monolith
If logic is domain/business critical, keep it in core services.
3. Optimize for the frontend’s use cases
Design endpoints around screen- or flow-level needs, not backend entities.
Example:
- Instead of
/users,/orders,/recommendations - Prefer
/home,/checkout-summary,/account-overview
This reduces:
- Chattiness
- Over-fetching
- Client-side orchestration
4. Minimize round trips
Use the BFF to:
- Compose multiple backend calls into one response
- Batch requests when appropriate
- Cache where safe
- Parallelize backend calls internally
This is especially important for mobile and high-latency networks.
5. Shape data for the client
Return exactly what the UI needs:
- Rename fields for clarity
- Flatten deeply nested structures if useful
- Convert backend data into UI-friendly view models
- Include computed display values when appropriate
Avoid forcing the frontend to “assemble” the response.
6. Keep authentication and session handling client-specific
The BFF is often a good place to:
- Manage tokens/cookies
- Exchange auth credentials
- Enforce CSRF protections for browser clients
- Bridge between cookie-based browser auth and token-based APIs
But be careful:
- Never store secrets insecurely
- Keep auth flows consistent and auditable
7. Make caching intentional
Use caching for:
- Static or slowly changing data
- Expensive aggregations
- Public content
Be careful with:
- Personalized data
- Authorization-sensitive data
- Invalidation complexity
Choose the caching layer deliberately:
- CDN
- BFF in-memory cache
- Distributed cache
- Backend service cache
8. Handle failures gracefully
Because the BFF often aggregates multiple services, partial failure is common.
Best practices:
- Timeouts per downstream dependency
- Circuit breakers
- Fallback responses
- Partial rendering when possible
- Clear error shapes for the frontend
Don’t let one failing backend take down the whole UI unnecessarily.
9. Keep contracts explicit and versioned
Define clear API contracts:
- OpenAPI/Swagger or similar
- Typed schemas where possible
- Stable field naming
- Versioning strategy for breaking changes
Since the frontend depends heavily on the BFF, contract discipline matters a lot.
10. Instrument everything
Track:
- Latency per endpoint
- Downstream dependency latency
- Error rates
- Cache hit rates
- Request volumes
- Correlation IDs across services
The BFF is a good place to observe frontend-to-backend behavior.
Architecture and implementation tips
Use BFF as an adapter, not a domain owner
Good BFF responsibilities:
- Response composition
- Response transformation
- Request fan-out/fan-in
- Client-specific auth/session behavior
Bad BFF responsibilities:
- Reimplementing billing rules
- Owning cross-system source-of-truth logic
- Storing business state that belongs elsewhere
Avoid too much duplication across BFFs
If multiple BFFs need the same transformation:
- Extract shared libraries carefully
- Or move reusable logic into an upstream service
- Don’t create a “shared BFF core” that becomes tightly coupled and hard to change
Use async patterns when helpful
For slow or optional data:
- Fetch in parallel
- Return critical data first
- Load non-critical sections later if the UX allows it
Secure the edge
Because BFFs face client traffic directly:
- Validate inputs strictly
- Rate limit
- Apply authz checks
- Sanitize output
- Protect against injection and SSRF in downstream calls
Common mistakes
- Creating a BFF that is just a proxy with no value
- Putting business rules in the BFF
- Having one BFF for all clients, making everything messy
- Duplicating backend logic everywhere
- Ignoring observability and timeouts
- Returning backend-shaped JSON to the frontend
- Not planning for versioning and contract changes
When BFF is a good fit
Use a BFF when:
- Multiple frontend types exist
- Frontend teams need autonomy
- The UI needs aggregated data from many services
- Client performance matters
- You need client-specific auth/session behavior
When BFF may be unnecessary
A BFF may be overkill if:
- You have a simple app with one frontend
- Backend APIs are already tailored enough
- The team is small and simplicity matters more than specialization
Rule of thumb
A good BFF:
- Reduces frontend complexity
- Minimizes network calls
- Shields the client from backend churn
- Stays thin and focused
- Improves UX without becoming a mini-monolith
If you want, I can also provide:
- a BFF checklist,
- a reference architecture diagram, or
- an example BFF implementation pattern in Node.js / .NET / Java.