Estructura de una app NodeJS

El como estructurar una app Node fue una de las cosas que mas me costo aprender y entender en su momento, sobre todo por que cada autor tiene una manera especial de hacerlo y la defiende a muerte. Obviamente, yo no voy a ser la excepción, pero por contrapartida voy a dar mi punto de vista personal de por que elijo hacerlo de dicha manera.

Estructura básica de un proyecto Node.

Particularmente, al utilizar ES6/7 extensivamente en mis proyectos, este no es soportado de forma nativa aun por Node, por lo tanto necesito utilizar babel para transpilar dicho código. Cuando se trata de una app "Hola Mundo" con un solo archivo *.js la cosa es fácil, y con un comando de babel estamos listos, pero en un proyecto Node real, en donde pueden llegar a existir decenas, o cientos de archivo *.js, es buena idea directamente transpilar directorios.
Babel, tiene muchas opciones, y entre ellas existe el flag --out-dir, que permite compilar todos los archivos *.js de un directorio a otro, por ejemplo, de un directorio /src a uno /build:

"build": "babel src --watch --out-dir build"

Es un poco incomodo tener que estar escribiendo dicho comando cada vez que realizamos modificaciones en nuestros archivos, y poco practico, por lo tanto se puede agregar la bandera --watch para que el transpilador este "mirando" los cambios y transpilando automáticamente.

Hasta este punto, se debería tener algo parecido a esta estructura:

Todos los archivo *.js que vayamos creando, deben estar dentro de la carpeta /src, y cuando se transpile (automáticamente 😉), la misma estructura de esta carpeta aparecerá en la carpeta /build, pero en código ES5 entendible por Node y el navegador.

Genial, entonces ejecutemos nuestra app 'Hola Mundo' del anterior articulo en esta nueva estructura "escalable".

import http from 'http'

const onRequest = (req, res) => {  
  res.writeHead(200, { 'Content-Type': 'text/html' })
  res.write('Hola Mundo')
  res.end()
}

http.createServer(onRequest).listen(3000)

console.log('El servidor esta corriendo en el puerto: localhost, en el puerto: 3000')  

En este punto, estamos listos para ejecutar la app. Iniciamos el script de compilación:

npm run build  

A diferencia del que utilizamos en el articulo anterior, este script no finaliza, y como indique anteriormente, se queda al pendiente de los cambios que realicemos sobre los archivos *.js. Finalmente, utilizamos el comando start (en otra ventana de la terminal), para iniciar la app:

npm start  

Y podemos observar que todo funciona genial:

Trabajando con módulos.

Los módulos no son algo nuevo para nosotros, al realizar:

import http from 'http'  

Estamos importando un modulo de la carpeta node_modules, específicamente el modulo http.
Si quisiéramos importar un modulo desarrollado por nosotros, por ejemplo llamado 'cosa', la sintaxis seria similar, solo que tendríamos que indicar el PATH en donde esta dicho modulo, algo como esto:

import cosa from './PATH/cosa'  

Muy fácil, no? No obstante, lo realmente interesante es saber crear nuestros propios módulos, y como exportarlos a estos.
Vamos a crear un archivo config.js, este va a guardar información referente a ciertos datos "sensibles" de nuestra aplicación, por ejemplo podría guardar los datos de acceso a tu servidor de correo, o a otro servidor, las direcciones a las bases de datos, etc... Seria el lugar en donde se concentraría información que se va a utilizar por toda la app y que no queremos "hardcodear" en nuestro código, por decirlo de alguna forma.
En nuestro ejemplo, tan solo va a contener el puerto de escucha de nuestra app, pero nos va a servir para aprender ciertas cosillas importantes:

module.exports = {  
    port: process.env.PORT || 3000
}

Lo que realiza acá, es utilizar el objeto process, el cual brinda información referente a la app, es interno de Node, y en caso de que este no este especificado por defecto en el proceso, utiliza el puerto 3000.
Esta información es exportada mediante la nueva sintaxis de ES6:

module.exports = NOMBRE_MODULO_A_EXPORTAR  

No hay que olvidarse, que para utilizar este nuevo modulo, hay que importarlo en nuestro app.js, de la siguiente forma:

import config from './config'  

y ahora utilizarlo, reemplazando el valor del puerto, por el valor dentro de nuestro modulo config.js:

Modularizando nuestra aplicación.

Ahora vamos a ir un poco mas allá, lo primero que vamos hacer es crear un archivo server.js, el cual va a contener toda la información que en este momento tiene nuestro archivo app.js, y lo vamos a exportar para poder ser utilizado en el resto de nuestra aplicación.
Se puede hacer de varias formas, podemos exportar el modulo completo server.js, o podemos exportar la función que necesitemos, esto es un guiño al encapsulamiento para los amigos de Java 😉. Pero, para exportar una función, necesitamos una función, y en nuestro flamante archivo de servidor no tenemos ninguna función, por lo tanto la creamos de la siguiente forma:

exports.startServer = () => {  
     //Cosas maravillosas.
}

Finalmente, el código de nuestro fichero server.js, tendría que ser muy parecido al siguiente:

import http from 'http'

import config from './config'

exports.startServer = () => {  
    const onRequest = (req, res) => {
        res.writeHead(200, { 'Content-Type': 'text/html' })
        res.write('Hola Mundo')
        res.end()
    }

    http.createServer(onRequest).listen(config.port)

    console.log('El servidor esta corriendo en el puerto: ' + config.port)
}

Acto seguido, en nuestro archivo app.js, tendremos que importar el modulo server.js, y llamar a la función startServer() de este:

import server from './server'

server.startServer()  

Y una vez mas, ir a localhost:3000, para ver nuestra aplicación corriendo:

El proyecto, tendría que tener la estructura que se muestra en la imagen anterior, y como se puede comprobar, la app se ejecuta correctamente, habiendo modularizado nuestro proyecto.

Por otro lado, se puede comprobar como en la carpeta /build, se encuentran los archivos *.js transpilados en codigo ES5 y respetando la estructura de la carpeta /src:

Respetando esta estructura podemos tener un proyecto escalable en el tiempo, y sobre todo entendible. Por otro lado, cuando el mismo se sube a un repositorio, en el archivo .gitignore se pueden establecer reglas para que no sean subidas tanto las carpetas de /build y /node_modules, y estas crearlas en el momento del deploy. Incluso, se puede crear un script "deploy" el cual realice toda la compilación en un mismo archivo *.js de producción.
Nuestra imaginación marca los limites de lo que podemos ir haciendo con Node.

De esta forma, es fácil modularizar nuestra app, y hacer bien las cosas desde un principio.

El código de este ejemplo, se encuentra en mi GitHub, en el siguiente link.

Hasta próximas entradas! 🙃

Show Comments

Get the latest posts delivered right to your inbox.