SPAs were revolutionary and ground-breaking in the way they changed frontend architectures and shifting the entire view of the data-model(pun intended).
A lot of UI and data-shaping work came into the browser which created client-specific needs. Let's discuss a few of them.
1. Different shapes of the payload - There can be case of over and under fetching when a generic API is used. It sends too much data for one client and too less for another, thus forcing clients to request multiple endpoints and waste bandwidth.
2. Juggling between microservices - SPAs often need to call multiple microservices, join results and shape them for the view. This increases complexity and hurts performance.
auth/token handling
3. Mobile device handling - Mobile apps, TV apps or web SPAs have different payload, caching and security requirements. One API fits all is bound to fail.
4. Organisational bottlenecks - With a single backend, the dependency of the frontend team for minor API changes results in slower delivery timelines.
Let's understand through an example.
Suppose you've an web-based e-commerce application which has an API /v1/products/102938 showing the details of a certain product as follows -
{
"productId": "PROD-102938",
"name": "ZenBeat Pro Wireless Noise Cancelling Headphones",
"description": "Wireless headphones with active noise cancellation, deep bass, and long-lasting battery life.",
"seller": {
"sellerId": "SELL-908",
"name": "ZenBeat Official Store",
"rating": 4.6
},
"variants": [
{
"variantId": "VAR-1",
"label": "Black",
"price": 9999,
"inStock": true
},
{
"variantId": "VAR-2",
"label": "Silver",
"price": 10499,
"inStock": false
},
],
"reviews": {
"averageRating": 4.4,
"totalReviews": 612,
"topReviews": [
{
"reviewId": "REV-1",
"rating": 5,
"comment": "Excellent sound quality and very comfortable for long use."
},
{
"reviewId": "REV-2",
"rating": 4,
"comment": "Great noise cancellation, battery life could be slightly better."
},
]
},
"faqs": [
{
"question": "Does this support fast charging?",
"answer": "Yes, it supports fast charging and provides up to 5 hours of playback with a 10-minute charge."
},
{
"question": "Is there a warranty?",
"answer": "Yes, it comes with a 1-year manufacturer warranty."
}
]
}
Now to render data for a response like this on a mobile app would be very different to that of a mobile app given the limited real estate on the device. Probably in a mobile app, we would show the "faqs" on click of a button which would be another API call.
With lesser and different set of response, we get
- a better UX
- faster rendering times
- save on network bandwidth
And this doesn't just apply to mobile, but for any other client which might work better with a different API response than a single monolith API.
To deal with this mismatch and give each client(TV, mobile, web) their own backend, emerged the pattern of Backend for Frontend(BFF).
The idea here is to have an intermediate layer between the client and the backend. And mind that, this layer is completely to assist the client i.e presentation logic and there is NO business logic involved here. Imagine this, instead of a single API gateway, we have multiple API gateways for every type of client.
Let's understand 2 different use cases of a BFF for different types of backends.
1. BFF and Monolith
With a monolith API architecture, the BFF does the work of filtering responses and provides a specific entry point as per the client needs.
Each BFF decides
1) what it needs to fetch
2) how it needs to fetch
3) what needs to be sent in
A mobile BFF can remove the responses which might be unnecessary to be displayed on the mobile screen, like reviews or FAQs, while a desktop BFF can keep those additional details.
Here, you might think, why would we fetch all kinds of data from the backend if we're eventually going to drop it?
Yes, you're not wrong. We can use a flag, like device=mobile and filter the responses sent from the backend accordingly.
But this also complicates the backend logic and we try to keep the backend as generic as possible.
And on top, having a BFF layer with all the responses coming in, gives the advantage of caching responses to be used later.
2. BFF and Microservices
With a micro-service backend architecture, the BFF only picks those services which it requires in the catalogue of services.
In the picture above, a mobile client would benefit from a AR service to imagine any product in a real-life 3D visual, which would be absolutely unnecessary in the web client.
Now let's discuss some pros and cons of this middle-man!
Advantages:
- There is now a support for different types of interfaces in their own isolated manner
- Any particular client specific tweaks can be executed much faster
- Since backend is not directly exposed, there can be filtering of any sensitive data at the BFF layer
- BFFs can also act as a mediator between the stack/protocols that is being used by the client and the server, thus making it seamless. For e.g., if a legacy client uses XML but gets JSON from the backend, BFFs can act as a transformation layer.
- Given different client interfaces are treated separately, the security concerns can be addressed separately and in a much better way. There need not to have all kinds of security checks for every client.
- BFFs help make a generic backend, putting the heavy load of customisation upon itself. And thus, acts as a request aggregator, making the client lightweight.
Disadvantages:
- On the same lines of the last advantage point, fanning out or sending requests out from a single source(BFF) to multiple micro-services concurrently, can be network heavy. The choice of language and runtime is therefore crucial. Languages with efficient, non-blocking concurrency models (such as Node.js, Go or reactive Java) are better suited for handling high levels of parallel I/O without incurring excessive thread or memory overhead.
- Code for multiple BFFs would mostly be the same(as seen previously, both web and mobile would need Product details and Seller details services) with minor tweaks here and there, which results in code duplication, which increases developer efforts.
- With the addition of a new moving layer, comes in the pain of managing, maintaining and monitoring it.
- Having a new layer adds an extra network hop and in turn increases the latency of the application. Some applications like e-commerce apps or fintech dashboards benefit from it given their presence on multiple devices. But this would really harm the performance of real-time systems like High frequency trading.
After this entire discussion, it is definitely a head-scratcher to decide when to have this BFF in your system. Here are some real use-cases where a BFF seems very much handy and useful.
1. When the client interfaces are significantly different from each other, BFF brings in a heap load of advantages. The backend stays generic and simple and multiple BFFs can serve the multiple clients as per their need.
2. Format of communication of the client is very different. Suppose a legacy client using XML to render information has a backend with JSON responses. Here the conversion can be handled by XML with the use of a XML BFF.
A BFF is not a default architectural choice(contrary to the actual need of a Best Friend Forever lol), but a conscious trade-off. It shines when client experiences diverge, payloads and protocols vary, and frontend teams need speed and autonomy, while keeping the core backend clean and generic, empowering modern, multi-device applications.



Top comments (0)