Node.js – Entendiendo nuestro servidor (II)

Después de mostraros cómo crear un servidor básico con Node.js, que fuera capaz de escuchar en un puerto concreto, aunque siempre contestara igual, nuestro conocido «Hola Mundo»,  seguimos avanzando en el aprendizaje de la programación de Javascript en el servidor.

Nuestro siguiente proyecto, es añadir a ese servidor la capacidad de servid paginas estáticas y rutear peticiones, a la vez que empezamos a poner un poco de orden en nuestro código.

Nuestro punto de partida:

Un servidor muy basico con node.js

Fijaros que encerramos todo nuestro código en una función (línea 6), incorporamos un módulo de node.js (linea 4), y terminamos llamando a nuestra función (linea 17)

Los módulos

Lo primero que nos vamos a plantear es ver como podemos partir nuestro código en diferentes ficheros; el motivo ya lo sabéis: mejorar la legibilidad de nuestro trabajo y sus posibilidades de reutilización.

Si observamos la linea 4, vemos que la funcionalidad de inclusión de código ya esta resuelta en Node.js; la función «require» accede a un archivo externo y lo lee, dejando en la variable dicho archivo .

Nosotros solo tenemos que aprender como crear esos módulos, y a continuación veréis lo fácil que es hacerlo; solo tenemos que modificar nuestra ultima linea asi:

Creando módulos

En la línea 17 hemos declarado que queríamos exportar la función «inicio» como resultado de este fichero; tras esto podemos crear un nuevo módulo, al que llamaré «start.js» con este código:

Modulo que utiliza modulo de node

En la línea 4 le indicamos que importe el fichero «server.js», y en la 6, podemos utilizarlo como función.

Habrá casos en donde nos interesará exportar mas de un objeto, y también lo podemos hacer, así como cambiar el nombre para la exportación; por ejemplo :Node.js - exportacion/modulos

En la línea 17 exportamos la función «inicio», con el nombre «inicio».  Naturalmente, la forma de usarla cambiara; en nuestro fichero «start.js», deberemos cambiar la llamada.

Utilizando mudulo en Node.js

La utilización de una forma u otra, solo viene condicionada por las necesidades de cada caso. Con ambas formas, todo lo que no se está importando queda oculto, como privado del módulo exportado, aunque esos detalles los veremos mas adelante.

Los módulos y los ficheros

Si hemos dicho que los módulos son ficheros, deberemos tener en cuenta donde los guardamos, y después, deberemos indicar los path correctamente cuando hagamos los «require».

En el ejemplo anterior nos referíamos a un módulo que se encontraba en un fichero en la misma ruta del que llamaba; por eso nos bastaba con indicarle:

var server = require(‘./server.js’)

Tambien podemos indicar rutas absolutas, aunque pienso que no es demasiado correcto, mas que nada, por la dificultad de mantenimiento. Solo tenemos que escribirlo directamente.

En windows seria:

 var server = require('C:\\Program Files\\aplicacion\\server.js')

En linux, podríamos escribir

var server = require('/home/usuario/aplicacion/server.js')

 En cada caso, utilizaremos los separadores correspondientes al SO; aunque en el caso de windows, al ser su separador la ‘\’ que tiene también la funcionalidad de carácter de escape, debemos utilizarla duplicada.

Igual que utilizamos el ‘.’ para referirnos al directorio actual, podemos utilizar el ‘..’ para referirnos al directorio superior al actual.

En caso que no se encuentre el fichero, se lanzara un error ‘MODULE_NOT_FOUND’

Buscando node_modules

Cuando hacemos una llamada a «requiere» indicando solo el nombre del fichero, incluso sin extensión, lo primero que hace es comprobar si el nombre facilitado corresponde a algún modulo del core; si no es asi, le añade la extensión «.js» y comprueba que no exista en el directorio actual, y si no es asi, busca en el directorio padre una carpeta llamada node_modules y dentro de ella, el fichero solicitado; si no lo encuentra, sigue repitiendo esa búsqueda subiendo directorio en directorio.

Por ejemplo, supongamos que estamos trabajando en el fichero:

/home/usuario/aplicacion/node/servicios/atencion.js

y, en ese fichero, hacemos

var aut = requiere('aut')

Se realizará búsqueda de los siguientes ficheros:

/home/usuario/aplicacion/node/servicios/node_modules/aut.js
/home/usuario/aplicacion/node/node_modules/aut.js
/home/usuario/aplicacion/node_modules/aut.js
/home/usuario/node_modules/aut.js
/home/node_modules/aut.js
/node_modules/aut.js

Hasta que se encuentre. Esto nos deja claro que si indicamos las rutas relativas de nuestros módulos, mejorará el rendimiento de nuestro servidor; y por razones históricas, la mejor opción es dejar nuestros módulos en la carpeta «node_modules»

Adicionalmente, Node puede buscar el módulo en:

  • 1: $HOME/.node_modules
  • 2: $HOME/.node_libraries
  • 3: $PREFIX/lib/node

Siendo $HOME  el directorio raíz del usuario, y $PREFIX el valor que se haya fijado en la configuración para node_prefix.

Con lo que sabemos hasta ahora, deberemos crear en nuestra aplicación una carpeta «node_modules» y mover a ella nuestro primer módulo «server»

node.js - moviendo a la carpeta de modulos

Y modificar el «requiere»

node.js - cambiando el requiere

indicandole la carpeta en la que se encuentra, o solo el nombre del módulo

node.js - requiere con nombre de módulo

Incluso, si quereis, sin la extensión

Node.js - require sin utilizar extension

….lo encontrará igual, aunque deberemos tener cuidado que el nombre que estemos utilizando, no coincida con alguno de los que vienen con Node,ya que en dicho caso, no se ejecutara nuestro módulo, ya que los del core tienen preferencia.

Resumen final de la resolución de «require»

En el manual de Node.js, en la pagina  https://nodejs.org/api/modules.html hay un resumen que me he permitido copiar aqui, traduciendolo un poco,pero que os aconsejo que vayais a esa pagina para aclararos las dudas.

require(X) en un módulo que esta en la ruta Y
1. Si X es un módulo del core,
   a. return el módulo
   b. STOP
2. Si X empieza por  './' o '/' o '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"

LOAD_AS_FILE(X)
1. Si X es un fichero, carga X como text/javascript.  STOP
2. Si X.js es un fichero, carga X.js como text/javascript.  STOP
3. Si X.json es un fichero, parsea X.json a JavaScript Object.  STOP
4. Si X.node es un fichero, carga X.node como addon.  STOP

LOAD_AS_DIRECTORY(X)
1. Si X/package.json existe ,
   a. Parsea X/package.json, y busca un valor "main" .
   b. let M = X + (json main field)
   c. LOAD_AS_FILE(M)
2. Si X/index.js existe, carga X/index.js como text/JavaScript.  STOP
3. Si X/index.json existe, parsea X/index.json a JavaScript object. STOP
4. If X/index.node existe, carga X/index.node como addon.  STOP

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
   a. if PARTS[I] = "node_modules" CONTINUE
   c. DIR = path join(PARTS[0 .. I] + "node_modules")
   b. DIRS = DIRS + DIR
   c. let I = I - 1
5. return DIRS

El cacheo

Para mejorar el rendimiento, Node.js cachea los módulos la primera vez que los carga. Eso permite que sucesivas llamadas a un mismo módulo no repitan la posible ejecución del código que contiene el módulo internamente.

Si necesitamos que una parte del código se ejecute cada vez que se carga, deberemos exportar esa parte como función, y ejecutar dicha función

En  nuestro siguiente articulo, empezaremos a ver como analizar las rutas pasadas, y como servir ficheros

Acerca de Miguel Garcia

Programador, Desarrollador web, Formador en distintas areas de informatica y director de equipos multidisciplinares.
Esta entrada fue publicada en Formacion, Javascript, NodeJs y etiquetada . Guarda el enlace permanente.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.