Logo de Node.js

Node.js

Herramientas

Conceptos

Es un entorno de ejecución de JavaScript, es un entorno donde se puede ejecutar JavaScript, construido con el motor de JavaScript V8 de Chrome. Esta orientado a eventos, asíncrono y monohilo, pero los procesos asincronos los saca del hilo principal y los ejecuta en otro hilo, para que no se bloquee el hilo principal.

Es un entorno de ejecución diferente al navegador, por lo que, el objeto window no existe, pero si existe el objeto globalThis que es la variable global tanto del navegador como de Node, en el navegador apunta a window y en Node apunta a global.
Es recomendable usar globalThis para que el código sea compatible con Node y el navegador.
Todo lo que es global sale del objeto globalThis, por ejemplo console.log es globalThis.console.log.

La extension .js de los archivos en Node, por defecto utilizan CommonJS. Se puede poner también la extensión .cjs para indicar de manera más explícita que es CommonJS.
Para utilizar ES Modules, se debe agregar la extensión .mjs a los archivos.
Para utilizar ES Modules en Node, se debe agregar "type": "module" en el package.json.

Existen paquetes dentro de node que permiten muchas funcionalidades, por ej, paquetes para trabajar con archivos en un servidor, conectarse a bases de datos, etc. Babel es uno de los paquetes que esta disponible en node.

Normalmente los modulos de node se dejan por fuera del git porque se pueden construir fácilmente en base al package.json.

Cuando queremos obtener y devolver datos mockeados, deberiamos devolver una promesa con los datos, porque si el día de mañana se traen los datos desde una API seguirá funcionando igual.

Para los callbacks en Node se usa el patron error first, que es el primer parametro que recibe el callback, si hay un error, se pasa como primer parametro, si no hay error, se pasa null como primer parametro y el segundo parametro es el resultado.

Paquetes

Para usar los paquetes de Node, es recomendable desde la versión 16, hacerlo usando node:nombrePaquete, ej: const os = require('node:os') en vez de require('os').

Paquete OS

Podemos saber información del sistema operativo.
Importamos const os = require('node:os') ó import os from 'node:os'

Paquete File System

Para trabajar con archivos y carpetas.
Importamos const fs = require('node:fs') ó import fs from 'node:fs'

Tambien se puede usar fs/promises para trabajar con promesas.

import fs from 'node:fs/promises';
fs.readFile('./nombreArchivo.txt', 'utf-8')
  .then((data) => console.log(data))
  .catch((error) => console.log(error));

Igualmente se puede usar async/await.

import fs from 'node:fs/promises';
async function leerArchivo() {
  try {
    const data = await fs.readFile('./nombreArchivo.txt', 'utf-8');
    console.log(data);
  } catch (error) {
    console.log(error);
  }
}
leerArchivo();

Paquete Path

Para trabajar con rutas de archivos y carpetas.
Importamos const path = require('node:path') ó import path from 'node:path'

Paquete Process

Es un objeto global que proporciona información y control sobre el proceso actual de ejecución. Tiene propiedades y métodos que permiten interactuar con el entorno de ejecución de Node y da información sobre el proceso actual de ejecución.
Importamos const process = require('node:process') ó import process from 'node:process'

NVM

Node version manager, manejar la instalación de versiones de node.

NPM

Node Package Manager, es el gestor de paquetes de Node, es un software que se instala en el sistema operativo y permite instalar paquetes de Node.

Npm tiene 2 partes, uno es el registro donde estan todos los paquetes y el otro es la linea de comandos, porque se pueden utilizar alternativas a la linea de comandos, puedes usar yarn o pnpm y seguir utilizando el registro de npm.

npx es lo mismo que ejecutar ./node_modules/.bin/
Permite ejecutar el comando de una libreria que no ha sido instalada.
Permite ejecutar comandos arbitrarios que estan en el npm package.
Permite instalar paquetes de forma global en una carpeta temporal y ejecutar el binario al vuelo
ej, ./node_modules/.bin/eslint --init seria lo mismo que ejecutar npx eslint --init

Es recomendable instalar las dependencias siempre de manera local y no global. De manera global solo se puede usar una versión de una dependencia y si se tienen varios proyectos con distintas versiones de las dependencias, se puede tener problemas.

Se puede crear un archivo .npmrc para configurar npm de manera local, esto sobreescribe la configuracion global.

package.json

Le dice a node y a nosotros como funciona la aplicacion, que dependencias son necesarias para pasar a produccion, que puedo descartar para pasar a produccion, etc. devDependencies del package.json son dependencias de desarrollo y estas no llegan a la versión de producción.

La semantica de versiones se maneja asi, X.Y.Z donde:
X es la versión mayor, es cuando rompe la compatibilidad hacia atrás.
Y es la versión menor, es cuando se añaden nuevas funcionalidades.
Z es la versión de parche, es cuando se arreglan errores.

Dentro del package.json y las dependencias:

Dentro de los scripts puedo poner el comando:
"phoenix": "rm -f package-lock.json && rm -rf ./node_modules && npm install --no-fund --no-audit"
Lo que hace es eliminar el package-lock.json y el node_modules para reinstalar los paquetes, normalmente se usa cuando se actualizan los paquetes.
El —no-fund es para que no muestre los mensajes de paquetes que piden donaciones y el —no-audit es para que no muestre los mensajes de paquetes que piden auditar, ambos se usan para hacer una pequeña optimizacion que puede ahorrar unos segundos en las instalaciones.

peerDependencies significa que no lo vamos a instalar nosotros, sino que vamos a depender de que el proyecto donde lo vamos a utilizar ya tenga instalada esa utilidad. El paquete necesita una dependencia de produccion que debe ser instalada por el usuario.

Nodemon

Es un paquete que permite reiniciar el servidor cada vez que se hace un cambio en el código.
Se instala en el proyecto como dependencia de desarrollo con npm install nodemon -D.
No es recomendable instalarlo de manera global.

Puedo crear un script en el package.json para ejecutar nodemon, por ejemplo:
"dev": "nodemon nombreArchivo.js"

Express

Es un framework de Node para crear servidores web.
Se instala en el proyecto como dependencia de producción con npm install express.

Utilidades

Script para mostrar un puerto disponibe

// paquete para hacer conexión con el protocolo tcp, es mas rápido que http
import net from 'node:net';

export function findAvailablePort(desiredPort) {
  return new Promise((resolve, reject) => {
    // crear el servidor
    const server = net.createServer();

    // escuchar el servidor en el puerto deseado
    server.listen(desiredPort, () => {
      // server.address().port nos permite saber el puerto
      const { port } = server.address();
      // cerrar el servidor
      server.close(() => {
        resolve(port);
      });
    });

    // si hay un error al escuchar el puerto
    server.on('error', (error) => {
      if (error.code === 'EADDRINUSE') {
        // el puerto 0 indica que el sistema operativo elija un puerto disponible
        /* no es recomendable en producción, siempre va a ser un puerto que este disponible y
          normalmente se redirecciona al puerto 80. */
        findAvailablePort(0).then((port) => resolve(port));
      } else {
        reject(error);
      }
    });
  });
}

Script para crear un servidor web (utilizando la utilidad anterior para encontrar un puerto disponible)

import http from 'node:http';
import { findAvailablePort } from './free-port.js';

// si quiero utilizar las variables de entorno
// antes de ejecutar el script, ejecutar en la terminal: export PORT=1234
const desiredPort = process.env.PORT ?? 3000;

// crear el servidor
const server = http.createServer((request, response) => {
  // este console.log se ve en la consola del servidor, no la del navegador
  console.log('request recibido');
  response.end('Hola mundo');
});

findAvailablePort(desiredPort).then((port) => {
  server.listen(port, () => {
    console.log(`Servidor escuchando en el puerto http://localhost:${port}`);
  });
});