Do We Really Need A Web API?
Tags: javascript, fullstack, apiless
Published: 2019-11-17 09:32:26

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](https://liaison.dev) 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:

```js
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.

![Diagram showing traditional vs Liaison architecture](https://thepracticaldev.s3.amazonaws.com/i/su95fnx48910a99wh8vc.png)

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](https://github.com/gothinkster/realworld) with Liaison, both the frontend and the backend, and [the outcome](https://github.com/liaisonjs/react-liaison-realworld-example-app) 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:

| Project                                                                           | Frontend | Backend | Shared |
| --------------------------------------------------------------------------------- |:-------:|:-----:|:----:|
| [react-liaison](https://github.com/liaisonjs/react-liaison-realworld-example-app) |   1374   |   356   |   66   |
| [react-mobx](https://github.com/gothinkster/react-mobx-realworld-example-app)     |   1937   |         |        |
| [react-redux](https://github.com/gothinkster/react-redux-realworld-example-app)   |   2050   |         |        |
| [vue](https://github.com/gothinkster/vue-realworld-example-app)                   |   2118   |         |        |
| [angular](https://github.com/gothinkster/angular-realworld-example-app)           |   2122   |         |        |
| [node-express](https://github.com/gothinkster/node-express-realworld-example-app) |          |   596   |        |
| [nestjs-typeorm](https://github.com/lujakob/nestjs-realworld-example-app)         |          |   967   |        |
| [koa-knex](https://github.com/gothinkster/koa-knex-realworld-example)             |          |  1535   |        |
| [hapijs](https://github.com/gothinkster/hapijs-realworld-example-app)             |          |  1872   |        |

> The numbers represent the amount of lines of code excluding comments and test suites. Count done on October 7th, 2019 using [Tokei](https://github.com/XAMPPRocky/tokei).

[Liaison](https://liaison.dev/) 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.