Architecture of modern corporate Node.js applications

Oh, it’s not for nothing that the title hints at Fowler’s nettle. And when did frontend applications become so complex that we started talking about high matters? Node.js… frontend … wait, but the node is on the server, it’s the backend, and the guys there already know everything!

Let’s take it in order. And immediately a small disclaimer: the article was written based on my speech at Ya.Subbotnik Pro for frontend developers. If you are doing backend, you may not discover anything new for yourself. Here I will try to summarize my experience as a frontender in a large enterprise, explain why and how we use Node.js .

Let’s decide what we will consider as a frontend in this article. Let’s leave aside the arguments about the tasks and focus on the essence.

The frontend is the part of the application responsible for the display. It can be different: browser, desktop, mobile. But there is always an important feature — the frontend needs data. Without a backend that will provide this data, it is useless. There is a fairly clear border here. The backend is able to go to databases, apply business rules to the received data and give the result to the frontend, which will accept the data, template it and give beauty to the user.

We can say that conceptually the backend is needed by the frontend to receive and save data. Example: a typical modern website with a client-server architecture. The client in the browser (to call it a thin language will no longer turn) knocks on the server where the backend is spinning. And, of course, there are exceptions everywhere. There are complex browser applications that do not need a server (we will not consider this case), and there is a need to execute the frontend on the server — something called Server Side Rendering or SSR. Let’s start with him, because this is the simplest and most understandable case.


The ideal world for the backend looks something like this: HTTP requests with data are received at the application input, at the output we have a response with new data in a convenient format. For example, JSON. HTTP API is easy to test, it is clear how to develop. However, life makes adjustments: sometimes one API is not enough.

The server must respond with ready-made HTML in order to feed it to the crawler of the search engine, give previews with meta tags for insertion into a social network or, more importantly, speed up the response on weak devices. Just like in ancient times, when we developed Web 2.0 in PHP.

Everything is familiar and has been described for a long time, but the client has changed — imperative client template engines have come to it. In the modern web, JSX rules the ball, the pros and cons of which can be discussed for a long time, but one thing cannot be denied — in server rendering you can not do without JavaScript code.

It turns out when you need to implement SSR by backend development:

Areas of responsibility are mixed. Backend programmers are starting to be responsible for mapping.

Languages are mixed. Backend programmers start working with JavaScript.

The way out is to separate the SSR from the backend. In the simplest case, we take the JavaScript runtime, put a self-written solution or framework on it (Next, Nuxt, etc.) that works with the JavaScript template engine we need, and pass traffic through it. A familiar scheme in the modern world.

So we have already let the frontend developers on the server a little bit. Let’s move on to a more important issue.

Getting data

A popular solution is to create universal APIs. This role is most often assumed by the Gateway API, which is able to poll many microservices. However, there are problems here too.

Firstly, the problem of teams and areas of responsibility. A modern large application is being developed by many teams. Each team is focused on its business domain, has its own microservice (or even several) on the backend and its own mappings on the client. We will not go into the problem of micro-frontends and modularity, this is a separate complex topic. Let’s assume that the client displays are completely separated and are a mini-SPA (Single Page Application) within one large site.

Each team has frontend and backend developers. Everyone is working on their own application. API Gateway can be a stumbling block. Who is responsible for it? Who will add new endpoints? A separate API super team that will be forever busy solving the problems of everyone else on the project? What will be the price of a mistake? The fall of this gateway will put the whole system down.

Secondly, the problem of redundant/insufficient data. Let’s see what happens when two different frontends use the same universal API.

These two frontends are very different. They need different data sets, they have a different release cycle. The variability of the mobile frontend versions is maximal, so we have to design the API with maximum backward compatibility. The variability of the web client is low, in fact we have to support only one previous version to reduce the number of errors at the time of release. But even if the “universal” API only serves web clients, we will still face the problem of redundant or insufficient data.

Each mapping requires a separate data set, which it is desirable to pull out with one optimal query.

In this case, a universal API will not suit us, we will have to separate the interfaces. This means that you will need your own API Gateway for each frontend. The word “each” here denotes a unique mapping that works with its own data set.

We can entrust the creation of such an API to a backend developer who will have to work with the frontender and implement his wishes, or, which is much more interesting and in many ways more effective, give the API implementation to the frontend team. This will relieve the headache due to the SSR implementation: you no longer need to put a layer that knocks on the API, everything will be integrated into one server application. In addition, by controlling the SSR, we can put all the necessary primary data on the page at the time of rendering, without making additional requests to the server.

This architecture is called Backend For Frontend or BFF. The idea is simple: a new application appears on the server that listens to client requests, polls backends and returns the optimal response. And, of course, this application is controlled by a frontend developer.

Leave a Reply

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