mrtinsal

May 16, 2021 ~ 6 min read

Introducción al desarrollo Web con JavaScript


Vamos a hablar un poco de lo que pasa detrás del telón en el protocolo HTTP, el cual usan para comunicarse navegadores y servidores web entre sí.

Este es un texto ante todo explicativo y más teórico que práctico. Quizá sirve para clarificar conceptos

Definiciones

  • Aplicación: sistema compuesto por programas con una interfaz que permite al usuario realizar determinada(s) tarea(s)
  • Programa: conjunto de funciones que se ocupan de algo más específico, que de por sí realiza "algo"
  • Módulo: también conjunto de funciones, pero destinado a componer programas. En teoría por sí sólo no hace nada. Se recomienda que sean unidades atómicas que en general se ocupan de una sola cosa, y pueden ser reutilizados en distintos contextos.
  • Backend: programas que son transparentes a la interacción del usuario. Mayormente aquellos que ejecuta el servidor.
  • Frontend: programas + presentación de cara al usuario
  • Render: dibujado. Conversión de código/especificación en objetos gráficos, que pueden ser interactivos

Arquitectura base

Cada aplicación consta de un servidor web atendiendo en un puerto, que no es el standard (80) ni está abierto al público.

Supongamos que miApp.com está delegado en nuestro server con IP 14.14.14.14

En ese server está corriendo localmente miApp en el puerto 3000.

Cuando un cliente (navegador) pida http://miApp.com (petición a 14.14.14.14 puerto 80), lo recibirá el proxy server nginx.

Mediante un virtual host, nginx tiene configurado que las peticiones a miApp.com las haga pasar por 127.0.0.1:3000.

Este es el setup aconsejado para las apps en Node ya que nginx maneja mejor algunas cosas de cara al público, implicancias de seguridad por ejemplo.

Frontend, Backend?

Nuestros programas tras el puerto 3000 componen el backend de nuestra aplicación.

Éste se encarga de recibir, procesar y dar respuesta a las peticiones HTTP que envía el navegador, por ejemplo:

GET /users

Mediante alguna lógica de rutas (router) el backend maneja qué programa debe correr de acuerdo al recurso solicitado.

Por lo general nuestro backend va a implementar una arquitectura REST, esto es, orientada a recursos "stateful" y verbos HTTP.

Al serle requerido por ejemplo, la lista de usuarios, el backend se encargará de buscarlos en el sistema de almacenamiento que estemos usando.

Normalmente al navegador le llega un documento HTML, que es lo que dibuja en la pantalla.

El backend se encarga de servir este documento ya sea sirviendo un archivo estático tal cual es, el resultado de un programa que renderiza un template, o envía mismo un programa del front-end que pida los datos y renderice todo por su cuenta.

Esto es muy común en aplicaciones web, se suelen hacer combinaciones de estas ideas. Va en gustos (client-side rendering vs server-side rendering).

También son muchos los casos, en que el navegador recibe un "híbrido" entre documento y template. Este documento a su vez carga un programa que "puebla" de datos al template (quizás también renderiza algunas partes) al ser ejecutado por el navegador.

Vamos a tomar este último caso para hacer un ejemplo: tenemos un archivo en http://miApp.com/index.html "estático":

<!doctype html>
<html>
<head>
    <title>Noticias</title>
    <script src="/js/repeater.js" type="text/javascript"></script>
    <script src="/js/app.js" type="text/javascript"></script>
</head>
<body>
    <ul id="noticias">
        <li>
            <strong>{titulo}<strong>: {copete}
        </li>
    </ul>
    <button id="refresh">Actualizar</button>
</body>
</html>

Al escribir http://miApp.com en el navegador, lo primero que va a hacer es conectarse al servidor y decirle:

GET /

Nuestro backend aquí se comporta como un simple servidor del archivo index.html

La línea le está diciendo al navegador que descargue el archivo miApp.com/js/app.js y acto seguido lo ejecute.

Ahora... la comunicación entre el navegador y el back-end no ocurre sólo a través de las acciones deliberadas del usuario, y no recibe sólo documentos HTML.

La mayoría de las aplicaciones web actuales comunican constantemente el frontend con el backend mediante llamadas asíncronas a endpoints de la API de éste y con los datos que reciben modifican el documento ya previamente dibujado en la pantalla. Por su lado el backend hace las acciones o modifica los datos pertinentes según lo que recibe del cliente.

Desde el punto de vista del frontend y la experiencia de usuario esta propuesta tiene grandes ventajas respecto al enfoque clásico, que hace unos 10 años era la norma, y aún hoy se sigue usando.

¿Por qué hacer una sola petición al servidor, esperar que este procese todo, arme la respuesta y consiguientemente se redibuje toda la página?

Si podemos simplemente hacer peticiones específicas de los recursos que queremos, y así recargar específicamente sólo las partes visibles que queremos!

En el ejemplo que veíamos, el archivo app.js podría tener el siguiente contenido

var getNoticias = getData.bind(null, '/noticias', updateNoticias);

// se ejecutará getNoticias() cuando esté totalmente cargado el documento HTML
document
    .addEventListener("DOMContentLoaded", getNoticias);
// también se ejecutará getNoticias() ante el evento "click" del botón
document
    .querySelector("#refresh")
    .addEventListener("click", getNoticias);

function getData(uri, callback) {
    var request = new XMLHttpRequest();

    request.open('GET', uri);
    // defino qué va a pasar cuando el request cambie de estado
    request.onreadystatechange = function() {
        if(! (request.readyState === 4 && request.status === 200)) {
            return;
        }
        // si la petición HTTP ya está lista y la respuesta es OK
        // llamo a updateNoticias() ya con el contenido que envió el backend
        callback(request.responseText);
    }

    // disparo finalmente que el request al backend
    request.send();
}

function updateNoticias(data) {
    var tpl = document.querySelector('#noticias li');
    var dataRepeat = document.createAttribute('data-repeat');
    dataRepeat.value = data;
    tpl.attributes.setNamedItem(dataRepeat);

    // finalmente escribí la respuesta en el atributo data-repeat del elemento <li> y dejo que el componente repeater.js haga lo suyo:
    tpl.repeat();
}

Vamos a hacer un repaso.

El navegador, al entrar a http://miApp.com le dijo al servidor "GET /".

Le pidió el recurso "/" también llamado raíz, o índice.

El servidor respondió: "OK" y le entregó un documento HTML.

El navegador, leyendo el documento encontró que necesitaba un par de scripts, asique se los pidió al servidor, quien se los dio como simples archivos.

Una vez que los tuvo, los ejecutó. app.js le dijo en sus líneas al navegador que debía conectarse asíncronamente con el servidor para pedirle el recurso /noticias asique nuevamente pronunció el comando HTTP: GET /noticias

Esta vez el servidor tuvo que trabajar un poquito más. El backend (más adelante vemos un ejemplo) tiene asociado que cuando se le pida el recurso /noticias, ejecuta un programa que obtiene estos datos y los entrega al navegador en forma de JSON.

Al tener estos datos el navegador ejecuta la parte correspondiente en que app.js le dice: escribilos acá.

Este es sólo un ejemplo simple, pero ilustra en pequeña escala lo que sucede miles de veces en casi cualquier aplicación web.

JS Everywhere!

JS Everywhere

Hay cientos de frameworks y herramientas del frontend que nos facilitan tareas comunes repetitivas como pedir recursos al servidor o actualizar lo que se ve en la pantalla (por mencionar algunas, React, Angular, Backbone, Ember, Svelte). Y también hay artillería para facilitar o "enmarcar" las tareas del backend (Socket.io, Express.js, KoaJS, LoopBack).

Pero lo interesante es una de las mejores características de ECMAScript (JavaScript para los amigos): la misma aplicación en el mismo lenguaje puede programar tanto el backend como el frontend.

Esto es el llamado full-stack.

También hay herramientas enfocadas en este setup, como Meteor, MEAN.io, Keystone, Electrode

Para más herramientas existe http://nodeframework.com

JS Everywhere

May 16, 2021

mrtinsal

@mrtinsal


Comments