Good Bye Web APIs (Español)

Ángel De La Cruz Sánchez
5 min readNov 24, 2020

--

Al crear una aplicación de una sola página o una aplicación móvil, normalmente necesitamos implementar una API web (REST o GraphQL) para conectar el frontend con el backend. Técnicamente, no es muy difícil, pero tiene algunas consecuencias desafortunadas.

Imagina dos planetas. El planeta “frontend” habla JavaScript y el planeta “backend” también habla JavaScript o cualquier otro lenguaje avanzado.

Ahora digamos que estos planetas necesitan colaborar extensamente para formar una “aplicación”.

Desafortunadamente, los planetas no pueden comunicarse entre sí directamente usando su idioma nativo y tienen que depender de un tercero llamado “API web” que habla un lenguaje mucho menos sofisticado.

De hecho, el lenguaje de la mayoría de las API web se limitan a una combinación de URL, algunos métodos HTTP (GET, POST, DELETE, etc.) y algo de JSON.

Las APIS web que hablan GraphQL son más avanzadas pero quedan muy por detrás de las posibilidades de un lenguaje de programación como JavaScript:

  • El paradigma de programación es procedimental o funcional (sin programación orientada a objetos).
  • Solo se admiten los tipos más básicos (olvídese de la fecha, el mapa, el conjunto, etc.).
  • Falta el concepto de referencia (solo se pueden pasar objetos por valor).

La colocación de un lenguaje rudimentario entre el frontend y el backend agrega mucho texto estándar y arruina la experiencia de desarrollo.

Otro problema es que una API web es una capa adicional de la que preocuparse. Debe diseñarse, implementarse, probarse, documentarse, etc. Y todo esto, francamente, es un dolor de cabeza.

Pero lo peor es que la capa de API generalmente te obliga a degradar la calidad de tu base de código. De hecho, es bastante difícil mantener tu código SECO y cohesivo cuando tu interfaz y tu backend están separados por una API web.

Ahora imaginemos que podríamos deshacernos de la API web. Imagine que el frontend pudiera comunicarse directamente con el backend utilizando su idioma nativo. ¿No sería genial?

La buena noticia es que hoy es posible gracias a un conjunto de bibliotecas llamado Layr .

¡Hola, Layr!

Con Layr , el frontend y el backend están físicamente separados (se ejecutan en diferentes entornos) pero lógicamente reunidos (es como si estuvieran en el mismo entorno).

¿Como funciona?

  1. El backend se compone de una o más clases cuyos atributos y métodos se exponen explícitamente al frontend.
  2. El frontend genera algunos proxies para las clases de backend y puede usar estos proxies como si fueran clases regulares de JavaScript.

Bajo el capó, Layr se basa en un mecanismo RPC . Entonces, superficialmente, puede verse como algo como CORBA , Java RMI o .NET CWF .

Pero Layr es radicalmente diferente:

  • No es un sistema de objetos distribuidos . Un backend de Layr no tiene estado, por lo que no hay objetos compartidos en la pila.
  • No implica ningún código repetitivo, código generado, archivos de configuración o artefactos.
  • Utiliza un protocolo de serialización simple pero poderoso ( Deepr ) que habilita características únicas como invocación encadenada, procesamiento por lotes automático o ejecución parcial.

Layr comienza su viaje en JavaScript / TypeScript, pero el problema que aborda es universal y podría ser adaptado a cualquier lenguaje orientado a objetos.

Ejemplo

Implementemos el ejemplo clásico de “Contador” para ver cómo se ve al construir una aplicación de pila completa con Layer.

Primero, implementamos el “modelo de datos” y la “lógica empresarial” en el backend:

// backend.js

import {
Component,
primaryIdentifier,
attribute,
method,
expose
} from '@layr/component';
import {ComponentHTTPServer} from '@layr/component-http-server';

class Counter extends Component {
// We need a primary identifier so a Counter instance
// can be transported between the frontend and the backend
// while keeping it's identity
@expose({get: true, set: true}) @primaryIdentifier() id;

// The counter value is exposed to the frontend
@expose({get: true, set: true}) @attribute() value = 0;

// And the "business logic" is exposed as well
@expose({call: true}) @method() increment() {
this.value++;
}
}

// Lastly, we serve the Counter class through an HTTP server
const server = new ComponentHTTPServer(Counter, {port: 3210});
server.start();

¡Oh mi! ¿Todo ese código solo para un simple ejemplo de “Contador”? Claro, parece exagerado, pero en realidad hemos implementado un backend de grado completo con un modelo de datos, algo de lógica empresarial y un servidor HTTP que expone todo.

Ahora que tenemos un backend, podemos consumirlo desde un frontend:

// frontend.js

import {ComponentHTTPClient} from '@layr/component-http-client';

(async () => {
// We create a client to connect to the backend server
const client = new ComponentHTTPClient('http://localhost:3210');

// We get a proxy to the Counter backend class
const Counter = await client.getComponent();

// Lastly, we consume the Counter
const counter = new Counter();
console.log(counter.value); // => 0
await counter.increment();
console.log(counter.value); // => 1
await counter.increment();
console.log(counter.value); // => 2
})();

¿Que está pasando aqui? Al invocar el método counter.increment(), se incrementa el valor del contador. Tenga en cuenta que este método no existe en la interfaz. Se implementa en el backend y, por lo tanto, se ejecuta en este entorno. Pero desde la perspectiva de la interfaz, el entorno de ejecución real no importa. El hecho de que el método se ejecute de forma remota puede verse como un detalle de implementación.

La clase Counter en el frontend se puede ampliar para implementar características que son específicas del frontend. A continuación, se muestra un ejemplo de cómo anular el método increment()para mostrar un mensaje cuando el contador alcanza un valor determinado:

class ExtendedCounter extends Counter {
async increment() {
// We call the `increment()` method in the backend
await super.increment();

// We execute some additional code in the frontend
if (this.value === 3)
console.log('The counter value is 3');
}
}
}

Así es como se ve cuando el frontend y el backend se reúnen. Bastante genial, ¿no?

¿Cuál es el truco?

¿Por qué todo el mundo crea API web cuando podríamos prescindir de ellas?

Hay una buena razón para implementar una API web, es cuando desea exponer su backend a un desarrollador externo. Pero seamos honestos, la gran mayoría de las aplicaciones no tienen este requisito. Y si resulta que necesita una API web, es posible agregarla posteriormente mientras continúa utilizando el enfoque “sin API” para todas sus necesidades internas.

Entonces no hay trampa. De Verdad.

Conclusión

La eliminación de la API web le permite crear una aplicación de pila completa mucho más rápido mientras aumenta la calidad de su base de código.

Al usar Layr en varios proyectos, incluidos algunos proyectos de producción, pude reducir la cantidad de código en un 50% en promedio y aumentar en gran medida mi productividad.

Otro aspecto importante es la experiencia de desarrollo. Dado que el frontend y el backend ya no están separados por una API web, tienes una sensación similar a desarrollar una aplicación independiente y es mucho más divertido.

--

--

Ángel De La Cruz Sánchez

Mi objetivo ha sido siempre crear soluciones tecnológicas innovadoras que impulsen el crecimiento.