Cómo compartir datos entre aplicaciones SPA

phones

Si eres desarrollador #HTML y tienes varias webs, has de saber que con el siguiente truco que explicamos podrás compartir información entre todas tus webs para sacar el máximo partido a la experiencia de usuario.

Introducción al localStore

Como buen desarrollador #HTML probablemente conozcas el objeto localStore de W3C y los métodos que ofrece. Con él puedes alojar información en el lado del cliente – en el navegador web – sin tener que recurrir al uso de las cookies y sus limitaciones.

Las cookies son un diccionario de datos con clave y valor que se guardan en el navegador del cliente durante un tiempo determinado y que son anexadas como cabeceras http hacia el servidor en cada petición.

Las cookies han sido y siguen siendo muy utilizadas para pasar información entre el navegador web y el servidor, ya sea para indicar credenciales o identificadores para el tracking de usuarios. Por este último motivo están en boca de los usuarios que se esfuerzan por mantener la privacidad, ya que es una herramienta muy potente para obtener información de los usuarios. Véase la guía de AEPD para el RGPD para informarse acerca de los derechos de privacidad de los usuarios.

En cualquier caso, estas cookies tienen limitaciones técnicas en cuanto a la cantidad de información y la estructura de datos que pueden alojar. Al estar diseñados para que se sean transmitidas como parte de la cabecera http su forma está limitada a una cadena de caracteres y unos pocos bytes.

La mayoría de los navegadores no soportan más de 50 cookies en un máximo máximo 4093 bytes por dominio. Es decir, puedes enviar 1 cookie de 4093 bytes o 2 cookies de 2045 bytes, etc.

Actualmente con la evolución de las Single Page Applications (SPA), véase los principales problemas de las SPA, en donde una serie de pantallas y lógica de javascript se empaqueta en un entregable que se ejecuta en el navegador del cliente, el tamaño de la información a guardar en el navegador del cliente puede crecer con facilidad. Tanto es así que ese límite de 4Kb puede quedarse muy pequeño si deseamos registrar datos de preferencia, historial o datos offline para el uso sin conectividad en una SPA. Para ello en la organización W3C que regula los estándares de la Web crearon la posibilidad de guardar más información en el lado del navegador a través de una interfaz denominada localStorage o sessionStorage. En principio, a través del localStore se tiene tanto tamaño como se necesite, pero requiere permisos de usuarios. Si no, por defecto se dispone de unos 10Mb para Desktop y 5 para Móvil. El usuario puede interactuar con el objeto localStore mediante el consumo de cualquiera de los siguientes métodos:

interface Storage {
  readonly attribute unsigned long length;
  DOMString? key(unsigned long index);
  getter DOMString? getItem(DOMString key);
  setter void setItem(DOMString key, DOMString value);
  deleter void removeItem(DOMString key);
  void clear();
};

Para acceder a tal objeto podemos utilizar el punto de entrada que ofrece el objeto window de HTML, siempre que el navegador sea lo suficientemente moderno:

miStorage = window.localStorage;
localStorage.setItem('clave', 'valor');
Las entradas en localStorage no se transmiten como cabeceras http automáticamente

La diferencia principal entre el uso de cookies y el objeto localStorage es que en el segundo caso no se transmitirán los valores al servidor de manera automática. Si deseamos enviar datos del localStorage al servidor deberemos añadirlo manualmente a las peticiones que se desee. En cualquier caso, es muy interesante el uso de localStorage para gestionar información del usuario gracias al tamaño que nos ofrece y a la privacidad y control que tiene el usuario sobre lo que ahí se almacene. Sin embargo, al igual que las cookies, la información del localStorage sólo está visible para aquellos scripts que se ejecuten en el mismo dominio en el que la instancia de localStore has sido creada. Obviamente esta restricción tiene mucho sentido para evitar la consecución de información mediante el uso de scripts maliciosos de otras webs.

LocalStore compartido entre dominios

En el caso que tengas varias webs del SPA, es probable que necesites compartir información entre las aplicaciones. Y puede que por algún motivo esa información no sea conocida a priori por el servidor o su  conjunto de APIs que dan servicio a dichas aplicaciones. Un ejemplo de este caso es la necesidad de implementar Single Sign On entre aplicaciones SPA. Aunque por poco que pensemos seguro que se nos ocurren más aplicaciones para esta técnica. Sea cual sea la motivación que nos empuja a incorporar un mecanismo para compartir información entre Apps os comentamos una buena solución que nos ha funcionado a las mil maravillas: cross-storage. Cross Storage es una librería en Javascript que permite compartir un localStorage entre varias aplicaciones SPA aunque estas aplicaciones tengan dominios diferentes (app1.com, app2.com,…) Su funcionamiento es muy sencillo y se basa en que cada una de las aplicaciones SPA incorpore un iframe contra un dominio compartido, por ejemplo shared.com en donde se guardará la información compartida. La creación e inicialización de este iframe es ajeno al usuario y la librería hará todas las gestiones por nosotros. Además, la propia librería es un wrapper de localStorage, de tal manera que utilizaremos la interfaz que ofrece para leer y escribir en el localStorage. Internamente Cross Storage tratará primero de utilizar el localStorage del dominio compartido y en segundo lugar usará el localStorage del dominio de la aplicación.

Configuración de Cross Storage

La librería está disponible en https://github.com/zendesk/cross-storage Para utilizarla se requieren dos pasos: configuración del dominio compartido y configuración del cliente.

Configuración del dominio compartido

El primer paso se trata de ubicar unos ficheros que deben servirse estáticamente en el dominio compartido, por ejemplo en shared.com. Estos ficheros darán servicio de ofrecer el localStorage dentro del iframe que utilizará la librería en el lado del cliente. La librería denomina a este conjunto de ficheros hub, y está compuesto por hub.js y hub.html. Dentro de hub.html debemos indicar de qué dominios aceptaremos peticiones al localStore compartido. Ejemplo:

<!doctype html>
<head>
  <title>Cross Storage Hub</title>
</head>
<body>
  <script type="text/javascript" src="hub.js"></script>
  <script>
    // Limit requests to any client running on .localhost:300x
    CrossStorageHub.init([
      {origin: /.*shared.com$/, allow: ['get', 'set', 'del']},
      {origin: /.*app1.com$/, allow: ['get', 'set', 'del']},
      {origin: /.*app2.com$/, allow: ['get', 'set', 'del']}
    ]);
  </script>
</body>
</html>

Una vez ubicados estos ficheros en dominio compartido y verificados que son accesibles vía http pasamos a configurar los clientes.

Configuración del cliente

La configuración del cliente requiere incorporar la librería como dependencia dentro de nuestras aplicaciones SPA. Ya sea mediante el uso de bower:

bower install cross-storage

O mediante npm:

npm install cross-storage

Una vez instalada la dependencia el consumo del localStore compartido se realiza mediante simples llamadas asíncronas una vez indicado sobre qué dominio compartido vamos a trabajar:

var storage = new CrossStorageClient('https://shared.com/hub.html');

storage.onConnect().then(function() {
  return storage.set('newKey', 'foobar');
}).then(function() {
  return storage.get('existingKey', 'newKey');
}).then(function(res) {
  console.log(res.length); // 2
}).catch(function(err) {
  // Handle error
});

Conclusión

De esta manera a través de Cross Storage, desde cualquiera de las apps podemos obtener información escrita desde otra aplicación, o modificar alguno de los valores alojados. En nuestro caso nos ha permitido incorporar un mecanismo Single Sign On con muy poco esfuerzo en aplicaciones SPA. Por citar algunas limitaciones son dos las que se nos ocurren que se podrían incorporar con cierta facilidad y dotarían de más funcionalidad a la librería:

  • Posibilidad de incorporar otros dominios no conocidos mediante alguna autenticación, en vez de usar una lista blanca de dominios conocidos previamente. Ya que esto requiere re-desplegar el hub con cada cambio.
  • O permitir registrar funciones o handlers que fueran llamadas automáticamente mediante un polling, con una determinada frecuencia, en el localStore compartido (o incluso con WebSockets) para obtener una experiencia cercana a tiempo real en el cambio de datos del localStore compartido.

Este es uno de los métodos que hemos usado para compartir información. Es probable que hayas encontrado otro en lo basto de internet. Si es así, nos encantará ver tu comentario de cómo has resuelto el mismo problema. ¡Saludos!

Autor: Ramón Arnau

Director de Arteco Consulting sl. Ingeniero Informático. Máster en Administración y Dirección de Empresas. Máster en Tecnologías de la Información. Auditor ISO 27001. ITIL.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *