Backend for frontend

Why are some APIs more convenient to use than others? What can we, as frontenders, do on our side to work with an API of acceptable quality? Today I will tell Habr readers about both technical options and organizational measures that will help frontenders and backenders find a common language and establish effective work.

Yandex.Market turns 18 this fall. All this time, the partner interface of the Market has been developing. In short, this is an admin panel with which stores can upload catalogs, work with the assortment, monitor statistics, respond to reviews, etc. The specifics of the project are such that you have to interact a lot with various backends. At the same time, data cannot always be obtained in one place, from one specific backend.

Symptoms of the problem

So, imagine, there was some kind of task. The manager goes with the task to the designers — they draw the layout. Then he goes to the backenders — they make some pens and write a list of parameters and the response format on the internal wiki.

Then the manager goes to the frontender with the words “I brought you the API” and offers to script everything quickly, because, in his opinion, almost all the work has already been done.

Do you notice anything strange? Camel, Snake and Kebab Case within the same pen. I’m not even talking about the fesh parameter. What exactly is a fesh? Such a word does not even exist. Try to guess before you reveal the spoiler.

Spoiler alert

Fesh is a filter by store ID. You can pass several IDs separated by commas. There may be a minus sign before the ID, which means that this store should be excluded from the results.

At the same time, from javasctipt, of course, I cannot access the properties of such an object through dot notation. Not to mention the fact that if you have more than 50 parameters for one place, then, obviously, you have turned somewhere wrong in your life.

There are a lot of options for an inconvenient API.

If the goods are found, we get an array. If one product is found, we get an object with this product. If nothing is found, then at best we get null. In the worst case, the backend responds with 404 or even 400 (Bad Request).

There are situations easier. For example, you need to get a list of stores in one backend, and store parameters in another. In some handles there is not enough data, in some there is too much data. Filtering all this on the client or making multiple ajax requests is a bad idea.

So, what could be the ways to solve this problem? What can we, as frontenders, do on our side to work with an API of acceptable quality?

Backend for frontend

We use React/Redux on the client in the partner interface. There is a Node under the client.js, which does a lot of auxiliary things, for example, throws to the initialState page for the Editor. If you have server-side rendering, it doesn’t matter with which client framework, most likely it will be rendered by a node. And what if we go a step further and do not contact the client directly to the backend, but make our own proxying API on the node, maximally tailored to the client’s needs?

This technique is commonly called BFF (Backend For Frontend). For the first time this term was introduced by SoundCloud in 2015, and schematically the idea can be depicted in this form:

Thus, you stop going directly to the API from the client code. You duplicate every handle, every method of the real API on the node and go exclusively to the node from the client. And the node already proxies the request to the real API and returns you the answer.

This applies not only to primitive get requests, but in general to all requests, including those with multipart/form-data. For example, a store downloads through a form on the website .an xls file with its own directory. So, in this implementation, the directory is loaded not directly into the API, but into your node handle, which proxies the stream into the real backend.

Do you remember that example with result, when the backend returned null, an array or an object? Now we can bring it back to normal — something like this:

function getItems (response) {

   if (isNull(response)) return []

   if (isObject(response)) return [response]

   return response

}

This code looks terrible. Because he’s terrible. But we still need to do it. We have a choice: do it on the server or on the client. I choose the server.

We can also map all these kebab and snake cases into a style that is convenient for us and immediately set the default value if necessary.

query: {
   ‘feed_shoffer_id’: ‘feedShofferId’,
   ‘pi-from’: ‘piFrom’,
   ‘show-urls’: ({showUrls = ‘offercard’}) => showUrls,
}

What other advantages do we get?

Filtering. The client gets only what he needs, no more, no less.

Aggregation. No need to waste the client network and battery to make multiple ajax requests. A noticeable gain in speed due to the fact that opening a connection is an expensive operation.

Caching. Your repeated aggregated call will not pull anyone once again, but will simply return 304 Not Modified.

Data concealment. For example, you may have tokens that are needed between backends and should not get to the client. The client may not even have the rights to know about the existence of these tokens, let alone their contents.

Microservices. If you have a monolith on your back, then BFF is the first step to microservices.

Now about the cons.

Increasing complexity. Any abstraction is another layer that needs to be coded, deployed, and maintained. Another moving part of the mechanism that can fail.

Duplicate handles. For example, multiple endpoints can perform the same type of aggregations.

The BFF is a boundary layer that should support general routing, user rights restrictions, query logging, etc.

To neutralize these disadvantages, it is enough to adhere to simple rules. The first is to separate the interface and business logic. Your BFF should not change the business logic of the main API. Second, your layer should transform data only if absolutely necessary. We are not talking about a self-sufficient all-encompassing API, but only about a proxy that fills the gap by correcting the shortcomings of the backend.

Leave a Reply

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