BFF — The secret of all great mobile architectures (Part I)

Viton
Wellhub Tech Team (formerly Gympass)
6 min readNov 3, 2020

The first version of our Native App used to request 3 different services through their APIs. Besides, there weren’t any new features, it was just a facelift of features that previously existed on our Web application: Sign-in and Check-in. In this way, it shouldn’t be so hard to request those already existent APIs.

This post is about requesting APIs that were designed for other clients instead of being designed for the mobile app, how it became a problem, and how we solved it.

Let’s start with our very first feature, Sign-in. There are only two steps in our sign-in flow: find a user and then authenticate it (if it's possible). Nevertheless, the user could be redirected to another flow depending on some use cases.

  • Blocked: There is something wrong with his/her information and we need to fix it through the support team. We have to give them options.
  • Locked: a security feature for users who use to write wrong passwords for several times.
  • Not found in our database

Whereas a user with enabled sign-in could have different flows

  • Sign-In with password
  • Sign-In through an external provider (Google Account, for example)

Well, several different statuses for a single user and we also have to interpret it to send him/her to the right flow. Additionally, when using HTTP status code to represent the resource we still have 4 different flows:

  • Blocked flow
  • Locked flow
  • Sign-in with a password
  • Sign-in with an external provider

The HTTP status code by itself doesn’t help us. Maybe some info inside the payload could do it.

It seems great, right? But it's not!

The field blocked_info is not a user data, it’s our company data. Maybe this shouldn’t be within the “user” resource. An API to get this information should be a good approach (although this could give a bad user experience, nested API requests), or even to deploy this information inside the app bundle (this could be even worse in case the info we want to give is outdated). So what?

For this first version, we’ve decided to set a “status” info surrounding the user’s payload with a view to avoid nested requests in the App and have such information inside the bundle, even if that “detracts” what we do expect from a REST API.

This is not a User API anymore. Yet, all the information we need is still relative to the user info. In order not to duplicate this resource or abstract it wrongly we decided to modify this payload only for mobile requests. An HTTP header could help to define this scenario:

Without the mobile header:

With header { “mobile”: “true” } :

That’s how we’ve addressed the issues in the Sign-in flow. You may have found it controversial and in fact, it is. At that time, there were many discussions about how we could be damaging our services in the long term. For the mobile team, it was not the perfect API either, it was the result of many discussions and concessions from both sides.

All in all, we can move from the Sign-in to the Check-in feature. In this scenario, we’ve had some similar issues with APIs that that didn’t provide all the info we needed at once and, as a consequence was not up to the level of experience that we would like to deliver. By requesting API “A” and depending on the “status” field, it was also necessary to request APIs “B” and “C”. As I previously said nested HTTP requests are bad for the user experience. Now imagine requesting 3 APIs and the callback hell they could cause in our codebase. In the user’s perspective, when with a poor internet connection, this can be even worse.

This is how a request looks like from a mobile client perspective. The latency is presented every time you fetch some data. On the right side of the image, there is an example of when you avoid nested requests to save some time from requesting two APIs for the same service.
If you opted for doing a nested request, that’s how it might be.
And finally a user with a poor network connection. Your API response is still the same but the requests itself are the problem. Any unwanted response at any point invalidates the previous successful requests, like a TIMEOUT error.

Our proposed solution here for Check-in was kind of the same as the Login API, but in this case, we’ve created a new route.

For both features, some error messages and other texts that should be presented on the app didn’t come from the API. That was another reason why we had so many discussions about this approach (we wanted texts translated in the API also). That would help even more the native app development, so as not to worry about outdated translated messages client-side. But still, it’s not the purpose of any of these API’s to return translated messages to the client. With that in mind, some messages were translated and some not.

Summing up, these were the needs of our mobile client:

  • Abstract business rules or calculations based on the payload as much as possible
  • Make as few requests as possible to get information for the screen
  • Pieces of information that are more willing to change (such as translations, company emails, and URLs) should be server-side

At the same time we wanted our API’s with:

  • Solid interfaces
  • The same behavior for all it’s clients
  • REST principles

Even with a short deadline of this first version in which was not possible to build an architectural change for our mobile platform, we’ve always discussed how it could be. Just imagine it:

A server-side application, in which the goal is to connect with the different API’s we’ve got and do the payload mutation we need for a mobile client. This could be deployed at any time and the changes would reflect in the mobile app features without having to release a new version. That’s the BFF (Backend for Frontend). That would allow us to meet the app needs and to protect our API’s from its clients.

It looked awesome and in our research, we’ve found some good examples of how it could be. It was time to build such a service. There were so many challenges in its implementation that it didn’t fit in this post. So here is the Part II.

--

--