Back to the homepage
Angular

Backend for Frontend… by Frontend?

What is Backend for Frontend?

Let’s start with a short introduction to API Gateway. It is a service mediating communication between clients and backend services exposing API.

Its most important roles and tasks include:

  • being the single point of contact between the backend (which is divided into many services) and the outside world,
  • hiding information about the division of  backend into services ( backend hidden behind the api gateway looks and behaves like a monolith from the client’s perspective)
  • acting as a reverse-proxy for backend services,
  • being a firewall,
  • possibility of full monitoring of network traffic,
  • caching, compression, load balancing, A/B testing,
  • authentication,
  • action aggregation (sending several requests to various microservices and aggregation of responses within the processing of a single request from a client).

Due to the last two points, we can see that this is more than just a proxy. The role of the gateway api is not simply a properly configured nginx, but a fully-fledged backend service that also contains the application logic.

Backend for frontend is a specific variant of the api gateway pattern, which is distinguished by the fact that a dedicated gateway (bff) is created for each client (client is understood here as a separate application, e.g. web application and mobile application or two web applications with different functionalities).

What does this change (comparing to the classic gateway api):

  • each client-bff pair can have a separate, customized API contract between them,
  • the logic previously concentrated in one gateway, is distributed to several services according to its purpose,
  • the failure of a single bff does not cut off the other clients from the system.

Backend for Frontend by Frontend

Now let’s take it a step further and transfer the responsibility for creating and maintaining your own bff to the frontend team (the client-bff pair is inseparable and in the case of scaling to multiple clients, the division of responsibility is as follows (see graphic).

This has some consequences. First of all, the field of contract negotiation between frontend and backend shifts to contract between bffs and microservices. Contracts and the full communication between client and bff become an internal matter for the FE team. Because of the greater responsibility in the FE Team, at least basic knowledge of the development and operation of server applications is also required. FE Team, at the expense of its own resources, takes on some work from the backend team (that’s important when human resources on the backend side are a bottleneck in the project).

How to make the most of customer and bff association

We can use our battle-tested technology stack for this:

 

 

 

 

When we are talking about multiple web clients within a single system (e.g. the popular case of a web application for a client and an administration panel for management) monorepo becomes a great solution.

By placing a web application and a bff within a single workspace you get, among other things, full support for typescript typing (ensuring consistency of contrasts between them). By placing multiple client-bff pairs within the same workspace, you can easily share reusable modules.

Implementation of BFF

NestJS, just like Angular, is based on splitting the application into modules. In our case, we recommend dividing it into modules in order to reflect the division of backend into microservices (1 Nest module per 1 microservice). Furthermore, additional modules will be added to the bff, in particular:

  • a module responsible for (pre-)authentication,
  • a proxy module (for simple handling of all requests which do not undergo any aggregation on the bff side).

Within a single module, an application can be divided into use-cases, which consist of a controller-service pair. A single use-case handles a single request coming from the client side. For further aggregation, each use-case can expose its functionality with other use-cases (e.g. by directly injecting this service, or by introducing an additional facade).

The aggregation mentioned above simply means that the use-case service, in addition to executing the request to the backend microservice, can also use the functionality provided by other use-cases, retrieve additional resources and use them to compose a response to the client’s request (an example of aggregating resource A and B into a single response AB in the figure below).

The code for each use-case service looks fairly similar and consists of:

  • importing an API contract,
  • injecting facades (or other services directly),
  • retrieving the main resource,
  • extracting identifiers of additional resources,
  • fetching additional resources (concurrently if possible),
  • aggregate the resources into a response for the client (consistent with the contract).

Additional features and anti-patterns

Bff can and/or should contain the following features:

  • integration with selected external services (e.g. authentication, captcha validation, internal error logging),
  • proxy for requests that do not require aggregation,
  • change the way authorization tokens are stored (e.g. if in bff – microservice communication the token is passed in the request header, then in client-bff communication the slightly more secure http-only cookies can be used)
  • filtering/forwarding errors from microservices,
  • logging requests/responses in http communication with microservices (especially helpful during development),
  • caching,
  • client-bff api-contract versioning,
  • request-retry,
  • SSR (angular universal),
  • all other security-related topics (throttling, CORS, cross-site request forgery protection, rate limiting, etc.),
  • mock response system for the client (using bff and client-bff contracts).

 

Bff anti-patterns:

  • execution of business logic (100% of business logic should be done on the microservices side, bff is not a microservice!),
  • aggregation of requests modifying data on the server side (risk of inconsistency),
  • several clients using one bff (then we are dealing with a classic gateway),
  • one client using several bffs,
  • authentication (verification of access to an action/resource already on the bff level).

Summary

There is no one-size-fits-all solution when it comes to communication between backend and frontend. Each approach has its advantages and disadvantages. The Backend for Frontend (by Frontend) pattern should work well especially if:

  • there is more than 1 client for microservice APIs,
  • we have extra frontend resources in the project team to maintain an additional application on the FE side, 
  • we want to simplify contracts describing communication with microservices while providing customized APIs for clients.

For frontend developers, it’s also a nice opportunity to get some baseline experience on how server-side applications are developed and run.

About the author

Mateusz Dobrowolski

Angular Developer at House of Angular. Mateusz is a Typescript sympathizer with several years of experience in developing Angular applications.

Don’t miss anything! Subscribe to our newsletter. Stay up-to-date with the latest trends, tips, meetups, courses and be a part of a thriving community. The job market appreciates community members.

Leave a Reply

Your email address will not be published. Required fields are marked *