Manuel Vila

Peace, Love & Software

Do We Really Need A Web API: Simplifying Communication Between Layers

Typically, when we build a single-page application, the frontend and the backend are living in two very separate worlds that are connected with a web API. Even if they are implemented with the same language (JavaScript), they cannot communicate directly using this language. They need something else in between, so we build a web API (REST, GraphQL, etc.), and that complicates everything.
First, the web API must be implemented on both sides: an API client in the frontend and an API server in the backend. Second, to transport the domain model between the frontend and the backend, we must constantly serialize and deserialize it. All this leads to a lot of code scattering, duplication of knowledge, boilerplate, and accidental complexity.
We get lost in translation.
Most of the time, web APIs are not functional requirements. They don't add any value to the product we are building. They are just a necessary evil so the frontend can communicate with the backend. But is that really the case? Wouldn't it be possible to get rid of these web APIs?

Liaison

I started building Liaison to achieve exactly that: getting rid of these "pain-in-the-ass" web APIs.
With Liaison, frontend and backend can communicate directly with the language they are implemented in. The frontend can call a method in the backend without further ceremony. It is just a regular JavaScript method invocation.
The way it works is simple: a frontend class can "inherit" from a backend class. So, when a method is called, if this method is missing in the frontend, the corresponding method is executed in the backend, automatically.
For example, let's say the frontend is executing the following code:
await user.follow(anotherUser);
Depending on the implementation of
follow()
, the execution can happen:
  • In the frontend, if the method exists only in the frontend.
  • In the backend, if the method exists only in the backend.
  • Or both in the frontend and the backend, if the method exists in the backend but is overridden in the frontend.
When a method is executed in the backend, the attributes of the involved instances (
user
and
anotherUser
) are transported to the backend, the method is executed, and if some attributes have changed during the execution, those changes are automatically reflected in the frontend.
Conceptually, it works like class inheritance. The only difference is that the inheritance occurs across two execution environments: the frontend and the backend. And when we call a method, it doesn't matter where the execution actually happens. For the developer, frontend and backend become one unified world.
So there is no need to build a web API anymore. We can still implement such an API if it is actually required (for example, we intend to open our application to third-party developers through a REST API), but for our own needs, we greatly benefit from doing without it.
Without the burden of building a web API, we can dramatically reduce code scattering, duplication of knowledge, and boilerplate. We can build faster, and better.

RealWorld Example

Is this approach working? I think it does. I built a RealWorld example with Liaison, both the frontend and the backend, and the outcome looks quite amazing to me: straightforward implementation, high code cohesion, 100% DRY, and zero boilerplate.
In terms of the amount of code, in case it matters, my implementation is significantly lighter than any other one I have examined:
The numbers represent the amount of lines of code excluding comments and test suites (count done on October 7th, 2019 using Tokei.)
Liaison is still at an early stage, and a lot of work remains so it can be used in production. I truly believe it is something worth trying, and I am actively working on it. I expect to release the first beta version in early 2020.

Tags

Comments

November 18th, 2019

In case you worry about the interoperability of Liaison with other environments (like many people did), please head over to my last article on the Liaison Blog:

https://liaison.dev/blog/articles/How-about-interoperability-oy3ugk

November 18th, 2019

This has been a common issue for a long time with web apps. It might be helpful to look at other frameworks that have been down this road before, like Meteor. This was one of the allures of Meteor 8 years ago, that you would write completely isomorphic client and server side code, and there is no API layer between them; you simply write as function calls as if it’s all on the same side.

I personally use Meteor, so I’ve been using this functionality for a long time. I generally feel like there’s been a lot of reinventing of the wheel over the last few years to replicate what Meteor has done (everything from reactive frontend frameworks to API abstraction to GraphQL subscriptions), but for those who don’t use Meteor, I can see this being helpful in achieving one of those aspects.

November 18th, 2019

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

November 18th, 2019

Certainly, Meteor and Liaison share some similarities, but they are quite different beasts.

I don’t think Liaison reinvents the wheel. If it reinvents something, it is the axle so we can build nicer wheels on top of it. What I mean is that Liaison operates at a much lower level than Meteor. It is neither a framework nor even a library. I like to define it as a “language extension”.

Liaison adds primitives to the JavaScript language to enable “cross-layer class inheritance”. This low-level approach doesn’t trap us in a framework, and it opens a lot of possibilities.

Topics of interest