Node.js

Hoy vamos a hacer nuestra primera prueba con Node.js. Para quien no lo conozca o haya oido hablar de él, es una tecnología de servidor basada en JavaScript. Sí, habéis oido bien: JavaScript. Node.js esta implementado con una arquitectura orientada a eventos lo que lo hace altamente escalable y soprendentemente rápido. Ademas, se basa en el motor de JavaScript V8 creado por Google y que sigue evolucionando. Al contrario que el JavaScript al que estamos acostumbrado Node.js no trabaja en el navegador (N. del A: ¡bien!) si no en el lado del servidor. Así que, todos aquellos que os dedicabais a back-end y creíais que os ibais a librar de JavaScript, vais a tener que replanteároslo. Y la verdad, es que nunca está de más conocerlo ya que muchas empresas punteras lo usan y muchas lo están adoptando.

Node.js viene por defecto con muchos módulos incoporados que nos harán la vida muy fácil, ya que podremos usarlos simplemente importándolos en nuestro código. Además, tiene un gestor de módulos (npm) que nos permite incorporar módulos de terceros. Sobre todo esto, os recomiendo leer algo de documentación sobre Node.js, ya que el objetivo de este post, es solo hacer un pequeño ejemplo y no explicar ampliamente Node.js. Evidentemente, intentaré explicar todo aquello que se haga en el código lo mejor posible.

Antes de empezar, no prometo el mejor código, ni el más limpio y el más optimo, pero si que intentaré explicarlo todo como ya he dicho. Al igual que para muchos de vosotros, esta es una de mis primeras pruebas.

Vamos a ello.

La aplicación que vamos a realizar tendrá un total de cuatro ficheros y será la típica aplicación con un formulario y que mostrará un mensaje. No he hecho ningún tipo de separación MVC, ni he utilizado ninguna librería de terceros ni extraña, he intentado hacerla lo más simple posible, pero a la vez, que se pueda escalar y trabajar de forma cómoda con ella. Los ficheros son los siguientes:

  • app.js: Fichero que realizará el importe de los módulos implementados por nosotros mismos y que lanzará el servidor implementado con Node.js.
  • server.js: Será el módulo que contenga el servidor que atenderá las peticiones y procesará dicha petición para obtener todos los datos.
  • router.js: Será el módulo que envíe la petición a una función u otra según la URL solicitada. Será una especie de controlador.
  • requestHandlers.js: Será el módulo que tenga las funciones que procesen las distintar peticiones y a las que llegaremos desde el “controlador”.

Vamos a empezar por el fichero “app.js” que será una especie de main de nuestra aplicación.

El código del fichero “app.js” será el siguiente:


var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {};
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/show"] = requestHandlers.show;

server.start(router.route, handle);

En las primeras líneas vemos como se importan varios módulos que vamos a crear posteriormente, ya explicaremos como.

Luego se ha definido una especie de mapa, que relacionará la ruta de nuestra petición con una función del manejador que va a ser llamada.

Y finalmente, lanzaremos la instancia de nuestro servidor para que empiece a atender peticiones.

El siguiente paso es el fichero “server.js”. Como ya he dicho va a ser un módulo que arrancará nuestro servidor y procesará las peticiones.

Lo primero que vamos a hacer es ver como definir un módulo que podamos usar nosotros e importar en otros fichero más tarde de forma fácil. Para crear un módulo tendremos que tener dos cosas en cuenta:

  1. Necesitaremos una función que será la que contenga el código de nuestro módulo.
  2. Necesitaremos hacer un export al final del fichero para poderlo importar en otros ficheros.

El código del fichero “server.js” será el siguiente:


var http = require("http");
var url = require("url");

function start(route, handle) {
    function onRequest(request, response) {
        var postData = "";
        var pathname = url.parse(request.url).pathname;
        console.log("Request received for " + pathname + ".");

        request.setEncoding("utf8");
        request.addListener("data", function(postPiece) {
            postData += postPiece;
            console.log("Recibido trozo POST '" + postPiece + "'.");
        });

        request.addListener("end", function() {
            route(handle, pathname, response, postData);
        });
    }

    http.createServer(onRequest).listen(8888);
    console.log("Server started.");
}

exports.start = start;

Las dos primeras líneas son imports de módulos que trae consigo Node.js. Nos ayudarán a montar nuestro servidor y a procesar las peticiones.

En segundo lugar tenemos la función “start” que es la que contendrá el código de nuestro módulo donde definiremos una función para procesar las peticiones y crearemos el servidor.

Dentro de la función “onRequest”, crearemos dos listeners para procesar la petición:

  • Listener “data”: Procesará la petición mientras va llegando la información ya que no sabemos el tamaño que tiene dicha información.
  • Listener “end”: Evento que se lanza cuando ha llegado toda la información.

Finalmente, la penúltima línea crea nuestro servidor poniendolo a la escucha en el puerto 8888 y recibe como parámetro nuestro método para procesar peticiones.

Como habéis podido observar, cuando el evento de recepción completa de la petición (end) se lanza, se llama a la función “route” que está definida por nosotros.

Siguiendo nuestro avance, vamos a generar el fichero “router.js” que tendrá ese enrutador o controlador que será el que le diga a cada petición que función tiene que llamar. Como al principio definimos nuestra variable manejador (handler) esto será bastante fácil y el código bastante simple.

El código del fichero “router.js” será el siguiente:


function route(handle, pathname, response, postData) {
    console.log("Starting to enroute request from " + pathname);
    if (typeof handle[pathname] === 'function') {
        return handle[pathname](response, postData);
    } else {
        console.log("Handler not found for " + pathname);
        response.writeHead(404, {"Content-Type": "text/html"});
        response.write("404 Not found");
        response.end();
    }
}

exports.route = route;

Aquí de nuevo hemos creado la clase en forma de módulo para poder importarlo posteriormente. en la función no hay más que una pequeña comprobación de si con la ruta se puede llegar a alguna función y la llamada a esta. En caso de error, simplemente nos mostrará el error típico de página no encontrada.

Y finalmente, tendremos el módulo que contendrá las funciones que se ejecutarán según la petición que nos haya llegado. Aquí yo voy a meter las vistas y demás (sin MVC) ya que esto es un ejemplo básico.

El código del fichero “requestHandler.js” será el siguiente:


var querystring = require("querystring");

function start(response, postData) {
    console.log("Start handler has been called.");

    var body = '<html>' +
        '<head>' +
        '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' +
        '</head>' +
        '<body>' +
        '<form action="/show" method="post">' +
        '<input type="text" name="text" />' +
        '<input type="submit" value="Submit text" />' +
        '</form>' +
        '</body>' +
        '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function show(response, postData) {
    console.log("Show handler has been called.");

    var text = querystring.parse(postData)["text"] || '';
    if (text === '') {
        response.writeHead(301, {Location: '/start'});
        response.end();
    } else {
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("You have sent: " + querystring.parse(postData)["text"]);
        response.end();
    }
}

exports.start = start;
exports.show = show;

Aquí hay más bien poco que comentar. Se declaran las funciones y se les asigna contenido. Una de ellas muestra un pequeño formulario y la otra procesa la petición mostrando un mensaje.

Como habréis notado, estas son las funciones que hemos linkado con las rutas de las peticiones en el objeto manejador en el fichero “app.js”.

Sobre el código no hay que decir mucho más, ya que en si es bastante simple, pero si que quiero añadir un par de notas más para que las tengáis en cuenta a la hora de diseñas vuestras primeras aplicaciones.

En algún sitio leí que todo el código en Node.js se ejecuta en paralelo menos el nuestro. Así dicho, suena un poco confuso, pero pensad por un momento que nuestras funciones en la clase requestHandler devolvieran las respuestas en un return para que otra función, más arriba, los pintará (evitando así tener que pasar el objeto request, por ejemplo). Aunque dichas funciones se podrían ejecutar en paralelo todas según el número de peticiones que haya, nuestro método encargado de pintarlas, solo lo podría hacer de una en una. Si añadimos a esto que podríamos ejecutar una función que tarde mucho en devolver un valor, tendríamos todas las peticiones paradas sin sentido.

Un metáfora de esto que leí, lamentablemente no recuerdo donde pero se me quedo grabada es: Imagina un rey que se levanta y le manda una lista de tareas a los sirvientes, estos podrán hacerlas todas de forma paralela, pero el rey solo podrá recibir los reportes de uno en uno cuando los sirvientes hayan terminado.

Como útlima cosa, os dejo un enlace de un tutorial bastante útil, bien explicado y en varios idiomas que también podéis seguir.

The Node Beginner Book

Nos vemos.

Node.js

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.