Manual de desarrollo en el lado del servidor con Node.js
Cuando se utilizan lenguajes de programación del lado del servidor como C o PHP, el código se procesa, por lo general, de forma secuencial. Esto significa que un servidor solo inicia una nueva orden cuando la anterior ya se ha ejecutado y resuelto. En este sentido se puede hablar de un procesamiento sincrónico de tareas, por medio del cual se bloquea la ejecución de otros códigos hasta que el proceso actual se haya resuelto. Esto puede ocasionar desfases importantes en el caso de acciones complejas como acceder al sistema de archivos, a bases de datos o a servicios web.
Es por eso que muchos lenguajes de programación y entornos de ejecución, así como las implementaciones que se basan en ellos, soportan la posibilidad de ejecutar procesos en paralelo dentro de un mismo proceso en forma de hilos de ejecución (threads), los cuales permiten resolver acciones mientras el resto del código sigue ejecutándose. La desventaja de este método, también llamado multihilos (multithreading), es que utiliza muchos recursos: cuantos más hilos se inicien, más tiempo de CPU y espacio de RAM serán necesarios. Requieren, además, un mayor esfuerzo de programación. La implementación de JavaScript en el servidor basándose en Node.js permite, sin embargo, solventar estos obstáculos.
¿Qué es Node.js?
Node.js es una plataforma de software con una arquitectura basada en eventos que permite utilizar JavaScript, originariamente desarrollado para su uso del lado del cliente, en el lado del servidor, de la misma forma como se usan PHP, Java, .NET, Ruby o Python para escribir código en el servidor. Suele usarse para el desarrollo de aplicaciones JavaScript del lado del servidor que han de procesar grandes volúmenes de datos en tiempo real y es muy popular en la realización de servidores web ligeros.
Este proyecto de software multiplataforma nace en 2009 de la mano del desarrollador Ryan Dahl y está basado en el motor JavaScript de Google V8, utilizado en el navegador Chrome. Patrocinado por la empresa Joyent Inc, especializada en virtualización y computación en la nube, y desarrollado por la Node.js Foundation, Node.js está hoy sujeto al programa de proyectos colaborativos de la Linux Foundation. Las versiones actuales están disponibles para Microsoft Windows, Mac OS y Linux (la versión inicial solo soportaba Linux).
Node.js contiene una biblioteca de módulos de JavaScript que se pueden cargar con una función muy sencilla y que están disponibles como piezas prediseñadas para su uso en el desarrollo de aplicaciones web. Un ejemplo de ellos es el módulo “http”, que permite crear un servidor web rudimentario utilizando una sola función. La instalación posterior de módulos adicionales también es posible gracias al gestor de paquetes integrado NPM (Node Package Manager).
La mayor ventaja de Node.js es su arquitectura dirigida por eventos, con la cual se pueden ejecutar códigos de forma asíncrona. Para ello se apoya en hilos de ejecución independientes y en un sistema de entrada y salida de datos (Input/Output, o simplemente I/O) separada que permite el procesamiento paralelo de más de una operación de escritura o lectura.
- I/O asíncrono: entre las tareas clásicas de un servidor se cuentan responder (servir) a peticiones, almacenar información en una base de datos, leer archivos en el disco duro y establecer conexiones con otros componentes de la red, acciones que se agrupan bajo la abreviatura I/O del inglés “input” (entrada) y “output” (salida). En lenguajes de programación como C o Java las operaciones I/O se ejecutan de forma sincrónica, es decir, una tras otra. Esto hace que el sistema I/O permanezca bloqueado hasta que el proceso actual finalice. Por el contrario, Node.js utiliza un I/O asíncrono por el cual las operaciones de escritura y de lectura se delegan directamente en el sistema operativo o a una base de datos, lo que permite ejecutar un gran número de tareas I/O en paralelo sin que se llegue a un bloqueo (blocking). Esto proporciona una enorme velocidad a las aplicaciones basadas en Node.js y JavaScript en muchos escenarios, aventajando a otros sistemas.
- Hilos de ejecución (single threading): para compensar los tiempos de espera propios de un sistema I/O sincrónico, aquellas aplicaciones del servidor basadas en lenguajes de programación clásicos del lado del servidor se apoyan en hilos de ejecución o subprocesos, aun con los mencionados inconvenientes de este concepto. Un servidor Apache HTTP, por ejemplo, inicia para cada petición entrante un nuevo hilo. La cantidad de hilos procesables, y, con ella, el número de peticiones que pueden responderse de forma paralela en un sistema multihilo síncrono, viene definida por la capacidad de almacenamiento disponible. Node.js, por el contrario, al utilizar un I/O asíncrono, solo necesita un hilo, de forma que se reducen de forma considerable tanto la complejidad como la carga sobre los recursos.
- Arquitectura basada en eventos: es la que permite el procesamiento asíncrono de operaciones I/O. Se basa esencialmente en un único hilo de ejecución que se encuentra en una cinta continua de acontecimientos, cuya tarea es esperar a que se produzcan sucesos y gestionarlos. Estos se pueden presentar como tareas o como resoluciones. Cuando este bucle de eventos registra una tarea, una petición a la base de datos, por ejemplo, la delega mediante una función llamada “callback” a un proceso en un segundo plano. De esta forma, esta tarea no se procesa en el mismo hilo del bucle de eventos y este puede esperar al próximo suceso. Cuando un proceso en el segundo plano finaliza, la resolución se devuelve con la función “callback” al bucle de eventos, que puede resolver la entrega del resultado.
Este modelo no bloqueador dirigido por eventos tiene la ventaja de que una aplicación no permanezca inactiva mientras espera a que se produzcan eventos. Esto permite, por ejemplo, que puedan ejecutarse varias peticiones a la base de datos al mismo tiempo sin afectar al funcionamiento del programa. Es así como Node.js se convierte en la mejor herramienta para realizar arquitecturas web que requieran diversas peticiones externas, aventajando así al procesamiento síncrono.
Nuestro artículo sobre "Deno: entorno de ejecución para JavaScript y TypeScript" también podría interesarte.
Tutorial de Node.js: instalación
Node.js se puede descargar desde su página oficial como Installer o como paquete binario, en función del sistema operativo. La fundación Node.js también pone a disposición, junto al código fuente del software, paquetes binarios para ARM y SunOS, así como un Docker Image. Para instalar Node.js en un ordenador Windows solo se necesita el Windows Installer (versión de 32 o de 64 bits). Además de la versión actual 6.5.0 también está disponible la LTS 4.5.0 (Long Time Support), que es la que se seguirá en este tutorial de Node.js. Haciendo doble clic en el archivo .msi se abre el asistente de instalación, donde se aceptan las condiciones de uso y se escoge la carpeta donde se guardará el archivo descargado, así como los componentes que se van a instalar. Para comprobar si Node.js se instaló correctamente, inicia el programa y ejecuta un primer código JavaScript sencillo. Podemos usar para ello la función console.log(), que se utiliza para introducir valores en la consola de Node.js. La línea de código que se encuentra en muchos tutoriales como ejemplo estándar es:
console.log('Hello world!');
Tras introducirla en la consola, confirma con la tecla “Enter”. Esta función indica a Node.js que ha de escribir en la consola la frase “Hello world!”. En la pantalla aparece esto:
Este ejemplo permite comprobar que Node.js funciona y que puede ejecutar código JavaScript, pero también se puede solicitar a la consola que resuelva una operación aritmética:
console.log (15 + 385 * 20);
Si quieres que la consola proporcione información detallada sobre la versión instalada de Node.js y sus módulos, se utiliza el siguiente comando:
process.versions
Módulos de Node.js
El entorno de ejecución de base de Node.js, el motor JavaScript para Chrome V8, permite escribir servidores web y otras aplicaciones de red de gran eficiencia en el popular lenguaje de programación, poniendo a su disposición una exhaustiva biblioteca de módulos de programación. Estos módulos básicos, integrados en el entorno de ejecución, engloban funcionalidades centrales tales como la interacción con el sistema operativo, la comunicación de red o los mecanismos de cifrado. El gestor de paquetes integrado NPM también permite instalar a posteriori otros módulos.
Módulos básicos en Node.js
Para cargar un módulo básico en una aplicación de Node.js solo se necesita la función require(), que espera un string (cuerda) o parámetro que indica qué módulo se ha de cargar. Si se trata de uno de los módulos básicos basta con escribir el nombre del módulo. En el caso de módulos que se instalan posteriormente, este parámetro ha de incluir la ruta que lleva al módulo, incluso si se encuentra en el directorio actual (en este caso basta “./”). La siguiente línea de código muestra el esquema fundamental con el que se cargan módulos en las aplicaciones de Node.js:
var nombredelmodulo = require('ruta/nombredelmodulo');
Si queremos subir el módulo básico “os” el código se escribiría así:
var os = require('os');
El módulo “os” ofrece diversas funciones con las cuales solicitar en la consola información relacionada con el sistema operativo. Si un usuario, por ejemplo, quiere saber cuánto espacio libre en byte queda en la memoria tendría que escribir esto:
var os = require('os');
var freemem = os.freemem();
console.log(freemem);
En la primera línea se carga el módulo “os” en la variable homónima “os”.
El fragmento os.freemem() en la línea 2 representa una de las funciones soportadas por el módulo “os” y sirve para informar de la memoria disponible en byte. El valor resultante se guarda en la variable freemem.
En la tercera línea, finalmente, se utiliza la conocida función console.log() para escribir en la consola el valor que se ha guardado en la variable freemem, resultando así:
La ventana de la consola de Node.js responde con 5 gigabytes disponibles de memoria. Otras funciones del módulo “os” como os.networkInterfaces() u os.hostname() permiten obtener una lista con interfaces de red o el nombre del host. En la documentación oficial del proyecto se encuentra disponible más información sobre las funciones del módulo “os” y de otros módulos básicos.
Instalar módulos de terceros
La instalación de módulos de extensión se realiza en Node.js con el gestor de paquetes integrado NPM (Node Package Manager), de tal forma que no es necesario descargarlos manualmente desde páginas externas y copiarlos en el directorio correspondiente. El proceso completo de instalación se reduce a una única línea de código que se ha de introducir en la consola del sistema operativo, no en la de Node.js. En sistemas operativos Windows esta consiste en el intérprete de líneas de comando cmd.exe, que se abre introduciendo en la ventaja Ejecutar (Windows + R) el comando cmd.
Solo se necesita el comando npm install y el nombre del módulo que se quiere instalar:
npm install nombredelmodulo
Todos los módulos opcionales disponibles para Node.js pueden encontrarse en la página web oficial de NPM. Si, por ejemplo, quieres instalar el módulo “colors”, entra en el directorio elegido (p. ej., NodeModules) con la consola del sistema operativo e introduce el siguiente comando:
npm install colors
Así se vería en la pantalla la instalación del módulo “colors” en la carpeta C:\NodeModules creada previamente:
Una vez instalado, el módulo se ha de cargar en la aplicación. Para ello se utiliza la ya mencionada función require(). El siguiente ejemplo muestra cómo se aplica el módulo de color para destacar cromáticamente las respuestas en la consola:
var colors = require('C:/NodeModules/colors');
console.log('hello'.green); // escribe texto en verde
console.log('I like cake and pies'.bold.red) // escribe en rojo y negrita
console.log('inverse the color'.inverse); // invierte los colores
console.log('OMG Rainbows!'.rainbow); // arco iris
En la primera línea del código se carga el módulo “colors” (C:/NodeModules/colors) en la variable colors. Las líneas 2-5 contienen cada una una función console.log(), cuyos strings se han de destacar de diferente manera con el módulo color. Las posibilidades son: color (.red, .green), destacar (.bold, .inverse) y secuencias (.rainbow).
Crea tu propio servidor web con Node.js
Una aplicación habitual de Node.js consiste en el desarrollo de servidores web ligeros. Un sencillo servidor “Hola mundo” se puede programar en unos pocos segundos gracias al módulo preprogramado “http”. Todo lo que necesitas son las siguientes ocho líneas de código JavaScript que puedes introducir en un editor de textos y guardar como archivo .js en cualquier directorio. En este tutorial el archivo se denomina webserver.js y se guarda en el directorio MyServer.
var http = require('http');
var port = 8080;
var server = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello world!');
});
server.listen(port);
console.log('The server is available at http://127.0.0.1:' + port + '/');
En la línea 1 se carga el módulo “http” mediante la función require() y se almacena en la variable homónima http:
var http = require('http');
En la línea 2 se define el número de puerto 8080 y se guarda en la variable port:
var port = 8080;
Desde la línea 3 hasta la 6 se define, con ayuda de la función createServer() del módulo http, una función callback, que acepta las peticiones entrantes del navegador (req) y las responde (res). Esta respuesta se compone del header que contiene el código de estado HTTP 200 (OK), así como la información de que el resto de la respuesta consiste en un texto. Este texto se incluye al final con la función res.end(). El contenido es “Hello World!”.
var server = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hallo Welt!');
});
La función listen() en la línea 7 sirve para conectar al servidor web con el número de puerto definido en la línea 2:
server.listen(port);
La línea 8 emite en la consola la dirección web donde está disponible el servidor web:
console.log('The server is available at http://127.0.0.1:' + port + '/');
En este ejemplo se trata de la dirección localhost 127.0.0.1 (el servidor web está, en este caso, disponible solo dentro del propio sistema). Para iniciarlo, ejecuta el archivo JavaScript recién creado en la consola del sistema operativo. Aquí se utiliza un comando siguiendo este esquema:
node path/script.js
Siguiendo este ejemplo este comando sería:
node C:\MyWebServer\webserver.js
Si se han introducido correctamente tanto los datos en el archivo como su dirección en la consola, esta responde con la dirección web del servidor:
El servidor web está preparado para recibir peticiones. Puedes probarlo introduciendo la dirección web 127.0.01:8080 en un navegador web. Si el servidor está correctamente programado la ventana del navegador mostrará nuestro mensaje de saludo “Hello World!”.