Server Side Rendering I - Conceptos


Intro

El otro día en el Máster Front End Lemoncoders, Erik Rasmussen nos dió una sesión muy interesante sobre server side rendering y cómo implementarlo usando React y Next. Eso nos ha animado a escribir una serie de posts en la que explicamos cómo funciona esto de manera detallada.

En este primer artículo de la serie vamos a centrarnos en  es explicar bien el problema que viene a resolver "Server side rendering" así como conceptos de base, en siguientes entrega nos meteremos de lleno en una implementación utilizando Next y React.

Esto suena a termino de moda, ¿para qué sirve?

Básicamente para cubrir dos áreas oscuras del desarrollo moderno:

  • SEO (Search Engine Optimization): es decir que los buscadores de Google te encuentren e indexen tu contenido.
  • Aumentar la velocidad en la carga inicial de tu sitio: es decir que la primera vez que accedas a tu sitio lo haga más rápido.

Cooomooor ¿me estás diciendo que una aplicación Angular o React tiene problemas para que Google las posicione? ¿Me estás diciendo que con todo lo moderno que son estos frameworks / librerías pueden tener problemas de rendimiento al servir la primera página? Correcto, veamos por qué.

El problema del SEO

Imaginemos la web de un sitio de reservas de hotel.

A nivel de SEO ¿Cómo funciona un crawler?  (esto es: los robotitos que van leyendo contenidos de internet para pasarle información a los buscadores).

crawler.png

 

¿Qué hace que un Razor / JSP o similar sea SEO friendly? Que todas las transformaciones se hagan en servidor y siempre acabemos sirviendo páginas HTML con un JavaScript asociado.

flujo_crawler_server.png

 

¿Qué hace una tecnología como React, Angular o Vue? Armar la página una vez que está en el lado cliente, ¿Qué pasa entonces con los crawlers?  Que se hacen un lío y no pueden leer bien el contenido de nuestra página.

crawler_jsx.png

 

¿Y por qué es tan importante esto del SEO? Si del éxito de tu empresa depende que tu contenido aparezca en Google es algo crítico, ejemplos: una web de reservas de hotel, vuelos, una tienda que vende productos... si lo que haces son aplicaciones de negocio (como puede ser el propio motor de reservas de un hotel, o una aplicación de banca online) no te hará falta, de hecho hasta hace no mucho se solía partir en dos un desarrollo web:

  • La parte de contenido, que la desarrollabas con una tecnología SEO friendly (por ejemplo un CMS) .
  • La parte de "chicha", la que tiene negocio, es decir, todo lo que hay detrás cuando le das click a comprar, reservar, operar...

Con server side rendering ya no nos hace falta realizar esta separación.

El problema de la primera carga

Vaaaleee te compro lo del SEO, pero... ahora ¿cómo me vas a decir que la primera carga en una aplicación moderna puede ser más lenta que con lenguajes de generación de markup en servidor tipo Razor o JSP? Veamos el porqué, cuando montamos una página con una tecnología de generación de markup en servidor, todo se genera desde recursos locales (o al menos servidores que tenemos a mano):

primera_carga_jsp.png

 

Cuando tenemos que servir por primera vez una página desde una tecnología moderna (React, Angular, Vue...), tenemos primero que cargar el HTML + JavaScript, y después dar otro salto para pedir los datos que nos hacen falta y ya pintar la página.

primera_peticion_jsx.png

 

Es algo así como si fueras a casa de tus padres y en vez saquearles la nevera de primeras, esperaras a volver a tu piso para pedirles que te envíen "los tupper" por mensajero.

¿No sería mejor que esos datos ya vinieran precargados? Eso es, veo que vas por buena camino... en esto consiste  "server side rendering".

Ok, lo entiendo pero ¿tan importante es esa diferencia de segundos? Aplícatelo a ti mismo, a que más de una vez eres impaciente cuando navegas por internet o incluso en mitad de la carga te arrepientes de comprar algo, una página que tarde más en cargar que la competencia se traduce en una página que vende menos, una página que sea un poco lenta en cargar se traduce en clientes que se arrepienten de haber clickado y se meten a otra cosa es decir... tu empresa pierde dinero y eso hace daño.

Y.. ¿Por qué las soluciones de generación markup en servidor no son buenas?

Seguro que ahora estarás pensando en: volvamos al JSP / Razor y dejémonos de modernidades...  pues esa solución nos no vale tampoco, veamos por qué:

  • Hoy en día las páginas en cliente tienen una funcionalidad muy rica, es decir mucha lógica en JavaScript, el Razor / JSP se basa en hacer una "fritanga" entre código de servidor y código de cliente, a más complejidad que tengan las páginas más complejo son de mantener (campos ocultos, datos que van de ida y vuelta, etc...).
  • Si siempre trabajamos con páginas basadas en Razor / JSP, la primera carga será más rápida, pero las subsecuentes no, tendremos que hacer un viaje a servidor, cargar la siguiente página, servirla... cuando es más fácil tenerlo todo precargado en una SPA y sólo pedir datos.
  • Además si vamos pidiendo páginas nuevas, se pierde el estado en nuestro navegador (o bien enviamos esta información a servidor y tenemos sesión en servidor, o tenemos que utilizar algún mecanismo tipo session storage, cookies...).
  • Otro problema adicional es que cargamos a nuestro servidor con el coste de tener que generar páginas HTML, ¿No es mejor que haga ese trabajo el navegador del cliente?

Ahora sí que me habéis liado del todo. ¿Podemos hacer algo que una lo mejor de los dos mundos? ...

Server Side Rendering al rescate

¿Y si tuviéramos una tecnología que nos permitiera en la primera petición servir directamente la página ya montada (incluyendo el estado de nuestras variables en JavaScript), y en subsecuentes que tirara siempre del navegador y sólo pidiera datos vía llamadas AJAX? Eso pinta bien, pero será complejo de narices, ya que yo puedo navegar por primera vez desde la página principal (por ejemplo la página principal de un sitio de reservas hoteleras), o que un amigo me pase una página concreta de ese sitio (por ejemplo la ficha de un hotel concreto), y que sea la primera vez que navegue en mi navegador... además de esto, ¿Tendría que hacer  un código específico en cliente y otro en servidor? ¡Vaya lío! ....

Aquí es donde entre la magia del server side rendering: ¿y si te digo que adoptando librerías y lenguajes adecuados puedes aprovechar la mayoría del código y apenas tener que hacer algún cambio ligero? ¿A que suena interesante?.

¿Cómo consigo que esto pinte en servidor y cliente?

 

Recuerdo hace unos años cuando era tan feliz programando con C# y .NET en servidor, y con JavaScript en cliente... y en eso alguien me dijo ahora vais a tener JavaScript en el servidor, y le conteste peeero que diiices eso no tiene futuro... bueno empecemos a ver porque JavaScript es el presente y el futuro (con el permiso de WebAssembly claro): si tu página web se monta en cliente con JavaScript, ¿Qué pasa si tu servidor se basa en el motor de Chrome y el lenguaje que utiliza es JavaScript? ... pues que se convierte en algo viable replicar los mismos pasos que damos en cliente, en el lado del servidor.

js_server_client.png

 

As que eso de tener JavaScript corriendo en el servidor cobra sentido.

¿Y las llamadas a API's REST...?

Lo suyo es que tengamos servidores de front end puros (que sirvan páginas, y que trabajen con JavaScript), y servidores que sirvan datos (por ejemplos API's REST o GraphQL, aquí usando el lenguaje en el que estemos más a gusto). En principio podemos hacer un fetch para pedir datos a esas API's tanto desde servidor como de cliente... hasta aquí parece que todo bien, pero nos podemos topar con un problema serio... LA SEGURIDAD: cuando estamos autenticados, tenemos cookies y headers que arrastran por ejemplos tokens de sesión, ¿Qué podemos plantear?

Opción 1: En el lado servidor no hay problemas en setear las cookies que hagan falta para poder hacer peticiones, en el lado cliente por seguridad el navegador web puede limitar el envío de esas cookies a una API REST que no esté en el mismo dominio (o no queremos liarnos la manta a la cabeza arrastrándolas y configurando servidores). En este caso lo que podemos hacer es que el propio servidor web de front end haga de "palanca", es decir tenga un proxy que a peticiones del mismo dominio (por ejemplo las que tengan como prefijo "/api" en la ruta), las redirija al servidor que toque, y esa respuesta se la envíe al cliente (digamos que es como si engañaramos al navegador).

proxy_server.png
Cuando montamos este proxy solemos configurarlo para que este cerca del servidor de API, y por otro lado las librerías de server side rendering suelen traer infraestructura para hacernos más fácil su implementación.

Opción 2:  Si usamos una API moderna podemos tener bien configurado CORS y arrastrar las cookies y demás, en este caso, la solución es mucho más directa, no nos hace falta servidor de proxy, ya que tanto cliente como servidor acceden a un servidor tercero de API's.

cors.png
Configurar bien CORS no tiene porque ser una tarea fácil, más cuando estamos trabajando con proyectos legacy o a los que no tenemos acceso, por eso la opción 1 es muy popular.

¿Y qué pasa con las variables que guardan estado?

Ese es un buen desafío, una vez que tu código se ha ejecutado en servidor y el HTML está servido, seguramente te haga tener en tu página de clientes valores de variables que ya estén informados, por ejemplo, imagina que tienes una lista de hoteles y la página en cliente (en React esto sería el estado de la aplicación), aquí lo que se hace es que se alimentan en un script que se ejecuta al inicio de la carga de la página en cliente.

Uy... esto parece que se puede complicar si tengo mucha información dinámica, ¿es así? Sí, para solucionar esto, tenemos una solución simple pero no fácil, si usamos Redux, tenemos todo el estado de la aplicación separado en un store, sólo necesitamos serializar ese estado en una variable global (deshidratarlo), y tener un script para que cuando se cree el store en la ejecución de la página de cliente lo restaure (lo rehidrata).

rehidratar.png
Lo de deshidratar/rehidratar, podíamos compararlo con los alimentos que viene en sobre y los mezclamos con agua para que tengan un aspecto medio decente, sería algo parecido a serializarlo / deserializarlo.

¿Qué diferencia tiene esto con ASP.NET / Razor / JSP...?

Esto punto es muy importante, y es el que más cuesta de entender:

  • En un sitio que corre con ASP.NET / Razor / JSP / PHP, lo que hacemos es que se fabrica una página en servidor, y al cliente siempre se sirve HTML/JS, el JS puede hacer sus llamadas AJAX y demás, pero cuando pedimos otra página, volvemos a ir a servidor, perdemos todo el estado que teníamos en cliente, y volvemos a servir una página nueva en la que empezamos de nuevo.

  • En un sitio que corre con server side rendering, la primera vez generamos una página con el HTML/JS necesario, una vez que estamos en cliente, hacemos llamadas AJAX para pedir los datos que hagan falta, y si queremos cargar otra página ya sólo cargamos  datos (el HTML ya lo teníamos precargado o lo pedimos como recursos al ser una aplicación SPA).

Es decir en server side rendering, la generación de página en servidor sería algo parecido al tanque de combustible auxiliar que lleva un transbordador espacial, sólo lo usa para la fase de despegue. Una vez que ha pillado altura se desprende de él y nunca más lo usará en ese viaje (para el resto de páginas que se quieran visitar, se encarga la instancia del navegador en renderizarlas). 

No pillo bien el flujo, ¿cómo va?

Como sumario de lo que hemos visto vamos a resumir el flujo en estos dos diagramas.

1. Cómo sería la carga de una primera página (el servidor genera y sirve HTML / JS).

primeracarga.png

 

2. Una vez que la primera página esta generada, nunca más volvemos a generar para esa sesión una página en servidor, ya se genera en cliente (y el cliente se encarga de cargar los datos vía llamadas AJAX), dependiendo de la implementación atacaremos un proxy server o directamente el servidor de API.

siguientes_cargas.png

 

Esto lo podemos comparar con cómo funciona el tanque de un transbordador espacial, sólo lo usamos para el despegue, una vez que estamos bien alto, el tanque se desprende, y tiras con tus propios motores.

Conclusión

Esto del server side rendering es algo muy potente, permite unir lo mejor de los dos mundos (generación de markup en servidor y en cliente), y lo que es mejor desarrollar con las tecnologías con las que estemos más a gusto sin tener que preocuparnos de posicionamiento web, ni perdida de rendimiento.

En el siguiente post de esta serie, pasamos de la definicíon de conceptos a la práctica, implementado una aplicación con server side rendering utilizando Next y React.

Agradecimientos

Este post cuenta con un revisor de lujo Erik Rasmussen, autor de propuestas y proyectos open source tales como: Redux-FormDucks Modular Redux, React Final Forms.

¿Con ganas de aprender desarrollo Front End?

Si tienes ganas de ponerte al día en el mundo del Front End impartimos un máster online con clases en vivo, más información: http://lemoncode.net/master-frontend