avatarAlbert Starreveld

Summary

The provided web content discusses the concept of a Backend For Frontend (BFF) as a solution to the inefficiencies and complexities introduced by microservices architectures in modern web development, particularly in the context of the upcoming cookie-less era.

Abstract

The article "What is a BFF? And how to build one?" delves into the architectural pattern known as the Backend For Frontend (BFF), which serves as an intermediary layer between front-end applications and a collection of microservices. It addresses the problem of "chatty" single-page applications (SPAs) and mobile apps that arise from the need to aggregate and filter data from multiple general-purpose APIs. The BFF pattern, first introduced at SoundCloud, simplifies front-end development by providing a tailored API that reduces the number of requests and the amount of data transferred, thus improving efficiency and maintainability. The article also explores the implications of the cookie-less era on authentication, suggesting that BFFs should handle authentication directly, eliminating the need for SPAs to manage tokens and perform silent renewals. The BFF, by being hosted on the same URL as the SPA, can leverage cookies for user context and token forwarding to downstream services. The article concludes by presenting three implementation options: using pre-built solutions like Duende or OidcProxy.Net, or building a custom BFF to meet specific requirements.

Opinions

  • The author suggests that microservices, while beneficial for agility, introduce complexity in front-end to back-end communication, making the BFF pattern a necessity rather than an option.
  • The BFF pattern is presented as a practical approach to streamline API interactions, reduce data transfer, and maintain a clean separation of concerns between front-end and back-end development.
  • The article emphasizes the importance of reevaluating authentication strategies in light of the upcoming restrictions on third-party cookies, advocating for BFFs to manage user authentication to simplify the process and ensure compliance with the new cookie-less policies.
  • The author provides a nuanced view of BFF implementation options, acknowledging the trade-offs between using off-the-shelf solutions and building a custom BFF, considering factors like licensing and specific project needs.
  • The article implies that the BFF pattern aligns with the broader trend of micro-frontends, suggesting that both patterns can work together to create a more modular and maintainable web architecture.

What is a BFF? And how to build one?

Who wants to build a monolith nowadays? Microservices are the way to go! But that’s no cheap alternative. It introduces all sorts of complexity. For example: Assume having several user-interface agnostic microservices, how “chatty” will your SPA or mobile app become?

General-purpose APIs might require consumers of APIs to aggregate and filter the responses of various endpoints to render something useful to the end-user.

And how will an architecture of multiple micro-frontends connected to a series of microservices impact maintenance? The promise of microservices is that they will make your application more agile. Create a new microservice as you please and tear one down when need be. But how will this impact the front end?

So, to solve that problem, yet another pattern occurred: The Backend For Frontend. The concept is what you might expect: One backend for every frontend that will act as an interface between a frontend app (SPA/Mobile) and the domain services. It’s an API that queries downstream services and transforms the response(s) to one single response formatted specifically for one specific front end. Schematically, BFFs in a microservice landscape might look somewhat like this:

A BFF + Microservices architecture

The BFF concept was first introduced at SoundCloud.

BFF, authentication, and the cookie-less era

You might wonder how that’s relevant to this article. But especially the cookie-less era makes the BFF pattern a little more complex than you would first guess from the picture above.

In many cases, downstream services require authentication. Usually, (access) token-based. The BFF needs to include these tokens in the requests that are forwarded downstream.

It’s a common practice to store access_tokens in the SPA. It includes it in the requests and, a presto, the API knows who’s executing the request. But there is going to be a problem with this approach in general. When a token expires (typically after one hour), SPAs usually, silently renew them. This mechanism is based on third-party cookies and those are not working anymore soon. As a result, silent renews will not work anymore in 2024.

Considering this, and the fact that the BFF requires the user’s access_token anyways makes authenticating to the SPA is a strange thing to do. With a BFF, the SPA is not supposed to communicate with a downstream service directly and because they’ll require authentication, a BFF will always need to include an access_token in requests downstream. So, from that perspective, it makes more sense to have the end-user authenticate to the BFF only. This eliminates the need to renew the token via the SPA (and the need to have tokens in the SPA at all).

This diagram illustrates what the authentication process with a BFF and some microservices look like:

BFF Authentication in a sequence diagram

To get this to work, the BFF and the SPA must be hosted on the same URL. That way, when the SPA initiates an HTTP request to the BFF, the HTTP requests will include cookies. The BFF needs those to get the user context (the access_token) and to forward it downstream.

So, how to implement it?

A BFF is a part of the front end. It is an API that reduces the number of API requests to downstream services and reduces the amount of data that travels over the wire between the front end to the BFF. It does that by translating and forwarding API requests to one or more generic APIs. It aggregates and transforms the response into responses that contain only what the front end needs.

This is still a pretty broad definition. That’s annoying. Because how will you implement it? To make matters more complex, Cam Jackson makes it even more complex:

https://martinfowler.com/articles/micro-frontends.html

So, by this definition, several approaches qualify. They range from full-fledged APIs that have dedicated storage to third-party software that forwards requests and transforms the responses if need be.

Option 1.) Duende

You don’t have to do it yourself. Several projects offer a free-to-use, off-the-shelf solution. For example, from the maintainers of IdentityServer, there’s a solution called Duende: https://docs.duendesoftware.com/identityserver/v5/bff/ (implementation samples here: https://docs.duendesoftware.com/identityserver/v5/samples/bff/)

By including Duende in a project, it will start working as a BFF. It implements the authentication mechanism, and it can be configured to forward requests to downstream APIs.

Option 2.) OidcProxy.Net

Duende is a great option for a BFF, but it has one downside: the license. Duende is free in some cases. To purchase it, Duende will make a custom quote for your organization. In some cases this results in a procurement-nightmare.

If you are looking for a similar approach, you might want to check out the OidcProxy.Net project:

Check out the GitHub project here:

Option 3.) Build it yourself!

Considering the non-generic nature of a BFF, it makes sense to build it yourself. You’ll need to do the following:

  • Create an API project
  • Implement authentication. Use the OpenId connect authorization code flow (with PKCE) and store the access token in a secure cookie.
  • Create API endpoints and grab the token from the cookie. Use it to invoke endpoints of the services you want to use.
  • Transform the response if need be.
  • Create a SPA and host it on the same site/project
  • Have the SPA post to the API endpoints you created earlier.
  • That’s it…

Summary

Communication between microservices and micro-frontends might not be too efficient. A microservice has a general-purpose API (if it has an API at all), so a front end might need to use multiple microservices to get things done and filter the information it needs to display from the responses. As a result, the front end gets chatty.

Also, when services get smaller, you’ll have more services to get things done. Invoking all of them in several front ends might not be desirable. It makes maintenance harder (versioning).

To solve these problems, at SoundCloud, they introduced an extra layer in their architecture. They would ship an API as a part of their front end. It acts as an interface between the front end and the services downstream. It aggregates or/and filters the responses into an efficient response.

This concept is called a Backend For Frontend.

Sources:

Bff
Micro Frontends
Microservices
Software Architecture
Basics
Recommended from ReadMedium