Introducción

¿Qué es Node.js?

Node.js® es un entorno de ejecución para JavaScript construido con el motor de JavaScript V8 de Chrome. Node.js usa un modelo de operaciones E/S sin bloqueo y orientado a eventos, que lo hace liviano y eficiente. El ecosistema de paquetes de Node.js, npm, es el ecosistema mas grande de librerías de código abierto en el mundo.

Node.js es un entorno de ejecución de código abierto basado en el motor de JavaScript V8 de Google Chrome. Permite a los desarrolladores ejecutar código JavaScript fuera del navegador web, lo que significa que pueden utilizar JavaScript para construir aplicaciones en el lado del servidor. Fue creado por Ryan Dahl en 2009 y desde entonces se ha convertido en una tecnología ampliamente adoptada y popular en el mundo del desarrollo web.

Historia

La historia de Node.js comienza en 2009 cuando Ryan Dahl, un ingeniero de software, presentó por primera vez Node.js en el evento "JSConf EU" en Berlín. La idea detrás de Node.js surgió de la frustración de Dahl con los servidores web tradicionales que utilizaban el modelo de hilos, que a menudo resultaba en un rendimiento deficiente y problemas de escalabilidad.

Ryan Dahl quería construir un entorno de servidor que pudiera manejar múltiples solicitudes concurrentes de manera eficiente y escalable. Para lograr esto, eligió utilizar el motor V8 de Google Chrome, que es un motor de JavaScript de alto rendimiento. Dahl desarrolló una capa de red y E/S sobre el motor utilizando el lenguaje de programación C++, y lo llamó "Node.js".

Fue lanzado oficialmente en 2009 y su enfoque en la asincronía y el modelo no bloqueante lo hizo popular rápidamente entre los desarrolladores. La comunidad de Node.js comenzó a crecer y aportar bibliotecas y módulos adicionales a través de NPM (Node Package Manager), que se convirtió en el gestor de paquetes estándar para Node.js.

En los años siguientes, Node.js continuó evolucionando y ganando popularidad. Grandes empresas como LinkedIn, PayPal, Netflix y Walmart lo adoptaron para sus aplicaciones y servicios, lo que contribuyó a su crecimiento y aceptación en la industria.

En 2015, dio un importante paso hacia la madurez con la convergencia de dos proyectos que habían surgido anteriormente: Node.js y io.js. Ambos proyectos trabajaron juntos bajo la iniciativa "Node.js Foundation" para consolidar esfuerzos y unificar la comunidad. Esto llevó a la creación de Node.js v4, que incluyó características y mejoras significativas.

Desde entonces, Node.js ha seguido mejorando y lanzando nuevas versiones de manera regular. Ha continuado siendo una tecnología popular para el desarrollo de aplicaciones web, servicios en tiempo real, aplicaciones de Internet de las cosas (IoT) y más. La comunidad activa de desarrolladores, las constantes mejoras de rendimiento y la amplia adopción en la industria aseguran que Node.js seguirá siendo una herramienta relevante y poderosa para el desarrollo en el futuro.

Algunos videos con más información:

Isomorfismo con JavaScript

El isomorfismo es una técnica de programación que permite ejecutar el mismo código en el lado del cliente y en el lado del servidor.

Hoy JavaScript, es el único lenguaje de programación capaz de ejecutarse en las 3 capas del desarrollo que tiene una aplicación web:

  1. En el Frontend con el JavaScript de toda la vida, el de los navegadores.
  2. En el Backend con entornos de programación como Node.js, Deno o Bun.
  3. En la Persistencia de Datos con gestores de bases de datos NoSQL como MongoDB, Couch DB, Firebase, etc.

Incluso hoy con JavaScript gracias a su entorno Node.js podemos controlar dispositivos de Hardware y IoT como controladores y chips tipo Arduino, robots, drones, wearables, electrodomésticos y más.

Veamos algunas de las características más importantes de Node.js en estos contextos de desarrollo.

Node.js en el Frontend:

En el contexto del desarrollo Frontend, Node.js tiene varios usos importantes:

  1. Ejecución de tareas en el entorno de desarrollo: Es útil para ejecutar tareas y scripts durante el proceso de desarrollo frontend. Por ejemplo, se puede utilizar para compilar archivos CSS y JavaScript, optimizar imágenes, concatenar archivos, minificar código, y otras tareas comunes que mejoran el rendimiento y la eficiencia de una aplicación web.
  2. Manejo de paquetes y dependencias: Junto con NPM (Node Package Manager), es ampliamente utilizado para gestionar dependencias y paquetes en el frontend. Con NPM, los desarrolladores pueden instalar y administrar librerías y frameworks de JavaScript que se utilizan en el frontend del proyecto.
  3. Entorno de construcción (build environment): Puede ser utilizado como un entorno de construcción para aplicaciones frontend. Es especialmente común en proyectos que utilizan frameworks de frontend como React, Vue.js o Angular, donde se pueden configurar scripts y tareas para compilar, empaquetar y optimizar el código antes de desplegarlo en producción.
  4. Desarrollo de herramientas frontend: Permite a los desarrolladores crear herramientas y utilidades que facilitan el desarrollo frontend. Por ejemplo, se pueden construir herramientas de generación de código, scaffolding (plantillas de proyecto), linters, y otras utilidades para mejorar la calidad y productividad del equipo de desarrollo.
  5. Servidores de desarrollo locales: Al utilizar Node.js, los desarrolladores pueden configurar servidores de desarrollo locales para probar su aplicación frontend antes de desplegarla en un servidor de producción. Esto les permite trabajar en un entorno seguro y controlado, y realizar cambios en tiempo real sin tener que depender de un servidor web externo.
  6. Pruebas y automatización: Es ampliamente utilizado en el desarrollo frontend para realizar pruebas unitarias, pruebas de integración y pruebas end-to-end. Además, se puede utilizar para automatizar tareas de prueba, lo que permite a los desarrolladores detectar y solucionar problemas rápidamente.
  7. Server-Side Rendering (SSR): Algunos frameworks de frontend, como React y Vue.js, permiten el Server Side Rendering (renderizado del lado del servidor). Node.js es esencial para habilitar este proceso, ya que puede ejecutar el código de estos frameworks en el servidor y enviar una página HTML completamente renderizada al cliente, lo que mejora el rendimiento y la experiencia del usuario.

En resumen, Node.js es una herramienta versátil y poderosa que desempeña un papel crucial en el desarrollo frontend moderno. Permite a los desarrolladores mejorar la eficiencia, gestionar dependencias, automatizar tareas y realizar pruebas para crear aplicaciones más eficientes y robustas.

Node.js en el Backend:

En el contexto del desarrollo Backend, Node.js tiene varios usos importantes:

  1. Basado en JavaScript: Utiliza el mismo lenguaje de programación, JavaScript, tanto en el lado del cliente (navegador) como en el lado del servidor (Node.js). Esto permite a los desarrolladores utilizar las mismas habilidades y herramientas en ambos entornos.
  2. Asíncrono y No bloqueante: Utiliza un modelo de entrada/salida sin bloqueo y basado en eventos, lo que permite a las aplicaciones manejar múltiples solicitudes y operaciones simultáneamente sin esperar a que una operación termine antes de pasar a la siguiente.
  3. Eficiente y Rápido: Gracias a su arquitectura no bloqueante, es muy eficiente y escalable, lo que lo hace adecuado para aplicaciones en tiempo real y de alto rendimiento.
  4. Escalabilidad: Se diseñó desde el principio con la escalabilidad en mente. Su enfoque en la asincronía y la eficiencia permite que las aplicaciones creadas, escalen fácilmente para manejar una mayor carga de trabajo sin aumentar significativamente los recursos del servidor.
  5. Amplia comunidad y ecosistema: Cuenta con una gran comunidad de desarrolladores y una amplia variedad de bibliotecas y paquetes disponibles a través de NPM (Node Package Manager), lo que facilita la construcción de aplicaciones con funcionalidades avanzadas y reutilizables.
  6. Manejo de solicitudes HTTP: Incluye un módulo http que permite crear fácilmente servidores web y manejar solicitudes y respuestas HTTP, lo que lo hace ideal para el desarrollo de aplicaciones web y APIs.
  7. Multi Plataforma: Es compatible con múltiples sistemas operativos, como Windows, macOS y Linux, lo que facilita la portabilidad de las aplicaciones desarrolladas en este entorno.
  8. Uso del mismo lenguaje en el cliente y el servidor: Al utiliza JavaScript tanto en el lado del cliente como en el servidor. Esto simplifica el desarrollo para aquellos que ya están familiarizados con dicho lenguaje, ya que pueden utilizar las mismas habilidades y conocimientos en ambos lados, lo que reduce la curva de aprendizaje.
  9. Ideal para aplicaciones en tiempo real: Gracias a su modelo no bloqueante y su capacidad para manejar muchas conexiones simultáneas, Node.js es especialmente adecuado para aplicaciones en tiempo real, como chats, juegos en línea, aplicaciones de colaboración y sistemas de notificaciones.

Estas características y ventajas hacen de Node.js una opción popular y poderosa para desarrollar aplicaciones web y servidores modernos y eficientes. Su ecosistema vibrante y en constante crecimiento lo convierte en una tecnología atractiva para desarrolladores de todo el mundo.

Node.js en las Bases de Datos:

Node.js tiene varios usos importantes en el contexto de trabajar con bases de datos:

  1. Conexión y consultas a bases de datos: Node.js permite establecer conexiones con diferentes sistemas de bases de datos, tanto relacionales como no relacionales, por ejemplo MySQL, PostgreSQL, MongoDB, Redis, entre otros. Utilizando bibliotecas y drivers específicos para cada base de datos, Node.js puede realizar consultas, inserciones, actualizaciones y eliminaciones de datos de manera eficiente.
  2. Desarrollo de APIs para acceso a datos: Se pueden crear APIs (Application Programming Interfaces) que actúen como intermediarios para el acceso y manipulación de datos en una base de datos. Estas APIs pueden ser utilizadas por aplicaciones cliente, tanto en el frontend como en otros servicios en el backend, para acceder y gestionar datos almacenados en la base de datos.
  3. Middleware para la manipulación de datos: En aplicaciones web, Node.js puede actuar como un middleware para realizar tareas de validación, transformación y procesamiento de datos antes de guardarlos o recuperarlos de la base de datos. Esto ayuda a mantener la lógica de negocio separada del acceso directo a la base de datos y permite una mayor modularidad y reutilización de código.
  4. Cacheo y almacenamiento en caché de datos: Se puede utilizar para implementar estrategias de caché, donde los datos frecuentemente accedidos se almacenan en memoria o en bases de datos rápidas como Redis. Esto reduce la carga en la base de datos principal y mejora significativamente el rendimiento de la aplicación, especialmente en aplicaciones con muchas solicitudes concurrentes.
  5. Procesamiento de datos en tiempo real: Es ideal para aplicaciones que requieren procesamiento de datos en tiempo real, como análisis de datos en vivo, actualización de datos, notificaciones, etc. Al ser asincrónico y no bloqueante, puede manejar grandes cantidades de datos y tareas en tiempo real de manera eficiente.
  6. Migraciones y cambios en la estructura de la base de datos: Con Node.js, se pueden crear scripts para realizar migraciones en la base de datos, lo que permite realizar cambios en la estructura de la base de datos de manera controlada y sin afectar la integridad de los datos existentes.
  7. Integración con bases de datos externas y servicios: Puede integrarse fácilmente con servicios y bases de datos externas a través de APIs o servicios web. Esto permite la comunicación con otras aplicaciones y sistemas para acceder y compartir datos de manera segura.

En general, Node.js es una herramienta valiosa para trabajar con bases de datos, ya que proporciona una plataforma eficiente y flexible para el acceso, manipulación y gestión de datos, lo que contribuye a la construcción de aplicaciones backend más eficientes, escalables y en tiempo real.

Node.js con el Hardware

Node.js también tiene aplicaciones interesantes en temas de hardware, permitiendo a los desarrolladores interactuar con dispositivos físicos y utilizar el poder de JavaScript para controlar y manipular hardware en diferentes contextos. Aquí hay algunas áreas en las que se utiliza:

  1. Internet de las cosas (IoT): Es una elección popular para desarrollar aplicaciones y prototipos de IoT. Su naturaleza asíncrona y no bloqueante lo hace ideal para interactuar con sensores y actuadores en dispositivos IoT. Puede comunicarse con dispositivos conectados a través de puertos GPIO (General Purpose Input/Output), UART (Universal Asynchronous Receiver/Transmitter), y otros protocolos de comunicación, permitiendo a los dispositivos IoT enviar y recibir datos.
  2. Robots y Drones: Se ha utilizado para controlar robots y drones. La capacidad de manejar múltiples tareas en tiempo real lo convierte en una opción atractiva para aplicaciones robóticas. Puede interactuar con sensores, motores y otros componentes para controlar el movimiento y realizar acciones específicas.
  3. Automatización del hogar: Se utiliza en proyectos de automatización del hogar, donde se puede conectar y controlar dispositivos electrónicos, electrodomésticos, iluminación y otros dispositivos inteligentes a través de interfaces web o aplicaciones móviles.
  4. Impresoras 3D: Se ha utilizado para controlar impresoras 3D. Puede enviar comandos de impresión a través de un puerto serial o una conexión de red para controlar el proceso de impresión y monitorear el estado de la impresora.
  5. Servidores para dispositivos embebidos: Node.js es una opción viable para implementar servidores en dispositivos embebidos con recursos limitados. Su huella ligera y su eficiencia lo hacen adecuado para manejar solicitudes de red y controlar la comunicación con otros dispositivos.
  6. Realidad Virtual y Realidad Aumentada: Se utiliza para construir aplicaciones en tiempo real que interactúan con dispositivos de realidad virtual y aumentada. Puede procesar datos en tiempo real de sensores y cámaras para proporcionar experiencias inmersivas.
  7. Hardware de prototipado y pruebas: Es útil para el prototipado rápido y pruebas de concepto en temas de hardware. Los desarrolladores pueden crear fácilmente interfaces para interactuar con diferentes componentes y evaluar la viabilidad de nuevas ideas y proyectos.

En resumen, Node.js proporciona una plataforma versátil y eficiente para interactuar con hardware, desde dispositivos pequeños y embebidos hasta robots, drones, y aplicaciones de IoT. Su enfoque en la asincronía y el desarrollo rápido lo hace una opción popular para proyectos que implican controlar y monitorear dispositivos físicos.

🔼 Regresar


Instalación

Tipos de instalación

Node.js ofrece diferentes tipos de instalación para adaptarse a las necesidades y preferencias de los usuarios. Los tipos de instalación más comunes son:

  1. Instalación Binaria (Binary): Esta es la forma más sencilla de instalar Node.js y es adecuada para la mayoría de los usuarios. Consiste en descargar un instalador precompilado específico para el sistema operativo y la arquitectura del usuario. Los instaladores binarios están disponibles para Windows, macOS y diversas distribuciones de Linux. Solo es necesario descargar el archivo del instalador y seguir el asistente de instalación para completar el proceso.
  2. Instalación a través del Gestor de Paquetes del Sistema (Package Manager): Algunos sistemas operativos y distribuciones de Linux ofrecen Node.js en sus repositorios y permiten instalarlo mediante el gestor de paquetes del sistema. Por ejemplo, en sistemas basados en Debian/Ubuntu, se puede instalar Node.js usando apt, y en sistemas basados en Fedora/RHEL, se puede instalar usando dnf o yum. Esta opción es conveniente para aquellos que prefieren utilizar las herramientas de gestión de paquetes nativas de su sistema.
  3. Instalación desde el código fuente (Source): Esta opción es menos común y se utiliza generalmente por desarrolladores avanzados o para personalizar la instalación de Node.js en sistemas operativos que no tienen un instalador binario disponible. Consiste en descargar el código fuente desde el repositorio oficial de GitHub, compilarlo y configurar el sistema manualmente.
  4. Manejo de versiones con NVM (Node Version Manager): NVM es una herramienta que permite instalar y administrar múltiples versiones de Node.js en el mismo sistema. Es útil cuando necesitas trabajar con diferentes proyectos que requieren versiones específicas. Se puede instalar en sistemas UNIX, macOS e incluso hay versión para Windows y facilita el cambio entre versiones de Node.js sin tener que desinstalar e instalar manualmente.

Las primeras tres opcines de la lista anterior se conocen también como instalación de tipo Stand Alone ya que instalamos una sola y única versión de Node.js, a diferencia de lo que nos ofrece NVM, la capacidad de gestionar múltiples de versiones.

Es importante mencionar que la forma de instalar Node.js puede variar según el sistema operativo y las preferencias del usuario. Para obtener instrucciones detalladas sobre cómo instalar Node.js en un sistema específico, es recomendable consultar la documentación oficial de Node.js o seguir las guías proporcionadas por la comunidad de desarrollo.

De cualquier manera aquí te dejo un par de enlaces y videos que te servirán para instalarlo:

Tipos de versiones

Node.js tiene dos tipos principales de versiones:

  1. Versiones LTS (Long Term Support): Estas son las versiones recomendadas para la mayoría de los usuarios, especialmente para aplicaciones y proyectos en producción. Las versiones LTS tienen soporte a largo plazo, lo que significa que recibirán actualizaciones de seguridad y correcciones de errores durante un período más prolongado. Node.js LTS se actualiza cada 6 meses, y cada versión LTS tiene un ciclo de soporte de 30 meses. Esto proporciona a los usuarios tiempo suficiente para planificar y realizar actualizaciones sin tener que lidiar con cambios frecuentes.
  2. Versiones Current (Actuales): Estas son las versiones más recientes de Node.js con las últimas características y mejoras. Se lanzan cada 6 meses y tienen un ciclo de soporte más corto en comparación con las versiones LTS. Estas versiones son ideales para aquellos usuarios que desean experimentar con las últimas características y estar a la vanguardia de la tecnología. Sin embargo, no se recomiendan para entornos de producción críticos debido a su ciclo de vida más corto y la posibilidad de cambios más frecuentes.

Es importante tener en cuenta que la elección de la versión de Node.js depende del tipo de proyecto y sus necesidades específicas. Si estás iniciando un nuevo proyecto, es recomendable usar la última versión LTS, ya que brindará una mayor estabilidad y soporte a largo plazo. Por otro lado, si ya tienes un proyecto en producción que utiliza una versión LTS más antigua, es posible que desees mantener esa versión y planificar una actualización a la siguiente versión LTS en el futuro.

Verificando la instalación

Una vez que se complete la instalación, puedes verificar si Node.js y NPM se han instalado correctamente abriendo una ventana de terminal y escribiendo los siguientes comandos:

node -v

npm -v

Si se muestran las versiones de Node.js y NPM en la terminal, significa que la instalación fue exitosa.

Si has decidido trabajar con NVM, estos son los comandos que más vas a usar:

// para mostrar la ayuda de nvm
nvm --h

// para listar las versiones de node que tienes en nvm
nvm list

// para instalar la versión vX.X.X de node con nvm
nvm install vX.X.X

// para usar la versión vX.X.X de node con nvm
nvm use vX.X.X

// para desinstalar la versión vX.X.X de node con nvm
nvm uninstall vX.X.X

// para asignar la versión vX.X.X como default en nvm
nvm alias default vX.X.X

Recuerda que debes remplazar las X.X.X por la versión que deseas instalar, no vayas a ejecutar el comando con las X's 🫢.

🔼 Regresar


REPL

El REPL (Read-Eval-Print Loop) de Node.js es una herramienta interactiva que permite a los desarrolladores ejecutar y probar código JavaScript de manera interactiva en tiempo real. Es una característica muy útil para experimentar con el lenguaje y probar fragmentos de código antes de incorporarlos en un proyecto más grande. Es similar a la consola interactiva que se encuentra en la mayoría de los navegadores web, pero se ejecuta directamente en la línea de comandos del sistema operativo.

Funcionamiento

  1. Read (Leer): El REPL espera a que el usuario ingrese una línea de código o una expresión JavaScript.
  2. Eval (Evaluar): Una vez que el usuario presiona Enter, el REPL evalúa la expresión o el código ingresado.
  3. Print (Imprimir): Después de evaluar la expresión o código, el resultado se imprime en la pantalla.
  4. Loop (Bucle): El REPL vuelve a esperar al usuario para ingresar otra línea de código y repite el proceso.

¿Cómo acceder?

Para acceder al REPL de Node.js, simplemente abre una ventana de terminal o línea de comandos y escribe node seguido de la tecla Enter. Esto iniciará el REPL, y verás un símbolo de > que indica que estás en modo de entrada del REPL. A partir de ahí, puedes ingresar cualquier código JavaScript, y el REPL lo evaluará y mostrará el resultado.

Ejemplo de uso

$ node
> 2 + 3
5
> const mensaje = "¡Hola, Mundo!";
undefined
> mensaje
'¡Hola, Mundo!'
> const cuadrado = (num) => num * num;
undefined
> cuadrado(5)
25
> let array = [1, 2, 3, 4, 5];
undefined
> array.map(num => num * 2)
[ 2, 4, 6, 8, 10 ]
> .exit

Para salir del REPL, simplemente escribe .exit y presiona Enter o usa Ctrl + C dos veces (para Windows). También puedes usar .help para ver una lista de comandos adicionales disponibles dentro del REPL.

El REPL de Node.js es una herramienta poderosa para experimentar y depurar código JavaScript de manera rápida y sencilla, y es especialmente útil para aprender y practicar conceptos del lenguaje.

🔼 Regresar


Hola Mundo

Para ejecutar un archivo JavaScript en Node.js, simplemente sigue estos pasos:

  1. Abre una ventana de terminal o línea de comandos en tu sistema operativo.
  2. Navega al directorio donde se encuentra el archivo JavaScript que deseas ejecutar. Puedes hacerlo utilizando el comando cd seguido de la ruta del directorio, por ejemplo:
    cd /ruta/del/directorio
  3. Una vez que estás en el directorio correcto, utiliza el comando node seguido del nombre del archivo JavaScript que deseas ejecutar. Por ejemplo, si el archivo se llama "app.js", escribe:
    node app.js
  4. Presiona Enter y Node.js ejecutará el archivo JavaScript. Cualquier salida o resultado del archivo se mostrará en la ventana de terminal.

Si el archivo JavaScript contiene código válido, Node.js lo ejecutará y mostrará el resultado en la terminal.

Recuerda que, al utilizar Node.js para ejecutar un archivo JavaScript, estás ejecutando el código en el entorno del servidor, que es diferente del entorno del navegador. Por lo tanto, algunas funcionalidades específicas del navegador, como el manejo del DOM, no estarán disponibles en el entorno de Node.js. Sin embargo, tendrás acceso a otras funcionalidades, como operaciones de archivo, solicitudes HTTP, acceso a la red y más que en los navegadores no existen.

🔼 Regresar


Blocking VS Non Blocking

En Node.js, el término "blocking" (bloqueante) vs. "non-blocking" (no bloqueante) se refiere a cómo el código se ejecuta en relación con las operaciones de entrada/salida (E/S). Estas operaciones de E/S pueden incluir lecturas/escrituras de archivos, solicitudes HTTP, conexiones de red, consultas a bases de datos y cualquier otra operación que implique esperar a que algo suceda, como recibir una respuesta del servidor o leer un archivo del disco.

Modo Blocking

En un modelo de ejecución bloqueante, una operación de E/S bloqueará la ejecución del programa hasta que se complete. Durante este tiempo, el hilo de ejecución se detiene y no puede continuar con otras tareas. Si hay múltiples solicitudes o tareas simultáneas que requieren operaciones de bloqueo, cada una debe esperar a que se complete la operación antes de continuar.

Un ejemplo simple sería leer un archivo de forma bloqueante:

const fs = require("fs");
console.log("Inicio del programa");
const data = fs.readFileSync("archivo.txt", "utf8");
console.log(data);
console.log("Fin del programa");

En este caso, la ejecución se bloquea mientras se lee el archivo y solo después de que se complete la lectura, se imprimirá el contenido del archivo y el mensaje "Fin del programa".

Modo Non Blocking

En contraste, en un modelo de ejecución no bloqueante, las operaciones de E/S se ejecutan de manera asíncrona. Cuando se inicia una operación, el programa continúa con otras tareas en lugar de esperar a que se complete. Cuando esta se completa, se ejecuta una devolución de llamada (callback) para manejar el resultado.

Un ejemplo no bloqueante sería leer un archivo de forma no bloqueante:

const fs = require("fs");
console.log("Inicio del programa");
fs.readFile("archivo.txt", "utf8", (err, data) => {
  if (err) throw err;
  console.log(data);
});
console.log("Fin del programa");

En este caso, la función fs.readFile inicia la operación de lectura y luego continúa con la ejecución del código sin esperar a que se complete la operación. Cuando la lectura del archivo se completa, se invoca la devolución de llamada y se imprime el contenido del archivo. Esto permite que el programa siga ejecutando otras tareas mientras se realiza la operación de lectura.

Ventajas del enfoque no bloqueante

La arquitectura no bloqueante es fundamental para su alta eficiencia y rendimiento. Al utilizar un solo hilo para manejar múltiples solicitudes, Node.js puede procesar solicitudes concurrentes de manera eficiente y escalable, lo que lo hace ideal para aplicaciones en tiempo real y de alta carga. Además, el modelo no bloqueante permite aprovechar al máximo los recursos del sistema, ya que el hilo de ejecución no se queda inactivo esperando operaciones de E/S, sino que se utiliza para otras tareas mientras las operaciones se realizan en segundo plano.

En resumen, la distinción entre blocking vs non-blocking en Node.js es clave para comprender cómo se manejan las operaciones de E/S y cómo influye en la eficiencia y escalabilidad de las aplicaciones desarrolladas en este entorno. El enfoque no bloqueante es esencial para aprovechar al máximo el rendimiento de Node.js en aplicaciones de tiempo real y de alta concurrencia.

🔼 Regresar


Single Thread

El concepto de Single Thread (hilo único) en Node.js se refiere a que, por defecto utiliza un solo hilo de ejecución para manejar todas las solicitudes y tareas. Esto es diferente de algunos otros entornos de servidor tradicionales que utilizan un modelo de múltiples hilos, donde cada solicitud se maneja en un hilo separado.

En Node.js, el hilo único es responsable de todas las operaciones y tareas del servidor, incluyendo la ejecución del código JavaScript, el manejo de operaciones de entrada/salida (E/S), y la respuesta a las solicitudes de los clientes. Esto incluye la ejecución de código en el servidor, la manipulación de archivos, las consultas a bases de datos, las solicitudes HTTP, entre otras tareas.

¿Cómo funciona?

El hilo único de Node.js utiliza un enfoque no bloqueante y asíncrono para manejar tareas y operaciones de E/S. Cuando una operación de E/S se inicia, en lugar de bloquear el hilo y esperar a que se complete, Node.js continúa ejecutando otras tareas y eventos. Cuando la operación de E/S se completa, se activa una devolución de llamada (callback) para manejar el resultado.

Por ejemplo, cuando Node.js recibe una solicitud HTTP, en lugar de crear un nuevo hilo para manejar la solicitud, el hilo único iniciará la operación de E/S para procesar la solicitud y luego continuará manejando otras solicitudes o eventos mientras espera la respuesta del cliente. Esto permite que Node.js maneje múltiples solicitudes concurrentemente utilizando un solo hilo.

Ventajas y desafíos

Ventajas

  • Eficiencia: El modelo de hilo único permite que Node.js utilice recursos del sistema de manera más eficiente, ya que no hay sobrecarga de conmutación de hilos y cada hilo puede manejar múltiples solicitudes.
  • Escalabilidad: La naturaleza no bloqueante y asincrónica de Node.js le permite manejar un gran número de solicitudes concurrentes de manera eficiente, lo que lo hace altamente escalable.
  • Menos complejidad: El enfoque de hilo único puede reducir la complejidad y facilitar la depuración y el desarrollo de aplicaciones.

Desafíos

  • Bloqueo de CPU: Si una operación bloqueante (por ejemplo, una operación intensiva de cálculo) se realiza en el hilo único, puede bloquear toda la aplicación, afectando la capacidad de manejar otras solicitudes en ese momento.
  • Necesidad de evitar bloqueo: Los desarrolladores deben tener cuidado de evitar bloquear el hilo único con operaciones de larga duración o bloqueantes, ya que esto podría afectar negativamente el rendimiento.

Es importante destacar que, aunque Node.js utiliza un solo hilo para manejar tareas, también puede aprovechar múltiples núcleos del procesador mediante el uso de la biblioteca cluster, que permite crear múltiples procesos hijos para distribuir la carga entre los núcleos del CPU y aprovechar al máximo los recursos de la máquina. Esto permite que Node.js mantenga su eficiencia y capacidad de manejo de múltiples solicitudes incluso en sistemas con varios núcleos de CPU.

🔼 Regresar


Event Loop

El Event Loop (bucle de eventos) es uno de los conceptos fundamentales de Node.js que lo hace único y poderoso. Es la piedra angular de la arquitectura no bloqueante y asíncrona de Node.js, lo que permite que el servidor maneje múltiples solicitudes de manera eficiente sin bloquear el hilo de ejecución.

El Event Loop es una estructura que se encarga de administrar y manejar eventos y tareas en Node.js. Funciona continuamente en segundo plano mientras la aplicación está en ejecución, y su objetivo principal es manejar las operaciones de entrada/salida (E/S) y las devoluciones de llamada (callbacks) de manera no bloqueante.

Cuando una operación de E/S (por ejemplo, una solicitud HTTP, lectura/escritura de archivos o una consulta a la base de datos) se inicia en Node.js, en lugar de bloquear la ejecución y esperar a que se complete, el Event Loop delega la operación a un componente externo del sistema operativo y continúa ejecutando otras tareas y eventos en el hilo principal.

Cuando la operación de E/S se completa, el componente externo notifica al Event Loop que la operación ha terminado. En ese momento, el Event Loop agrega la devolución de llamada asociada a la operación de E/S a la cola de eventos y, en algún momento posterior, cuando el hilo principal está libre, el Event Loop procesa la cola de eventos y ejecuta las devoluciones de llamada correspondientes.

Fases del Event Loop

El Event Loop consta de varias fases que se repiten continuamente mientras la aplicación está en ejecución:

  1. Fase de Lectura y Ejecución de Operaciones Externas: En esta fase, el Event Loop inicia operaciones de E/S y solicitudes externas, como lectura/escritura de archivos, operaciones de red y llamadas a bases de datos.
  2. Fase de Ejecución de Callbacks: En esta fase, el Event Loop procesa las devoluciones de llamada (callbacks) de las operaciones de E/S que se completaron. Las devoluciones de llamada se agregan a la cola de eventos y se ejecutan de manera secuencial.
  3. Fase de Procesamiento de Promesas (Microtask Queue): A partir de Node.js v12, esta fase fue introducida como un microtask queue independiente. Aquí se procesan las promesas y otros microtasks antes de pasar a la siguiente fase del Event Loop. Esto garantiza que las promesas se resuelvan antes de que se continúe con el procesamiento de las operaciones externas.
  4. Fase de Comprobación (Check): En esta fase, se ejecutan ciertos tipos de callbacks, como setImmediate(). Es una oportunidad para ejecutar operaciones más cercanas al final del bucle de eventos.
  5. Fase de Cierre (Close): Esta fase se encarga de manejar las tareas de limpieza y cierre de recursos antes de que la aplicación se detenga o se cierre.

En resumen, el Event Loop es el componente esencial de la arquitectura de Node.js que le permite manejar operaciones asíncronas y no bloqueantes de manera eficiente. Es la clave para el rendimiento y la escalabilidad de Node.js y es lo que lo convierte en una excelente opción para aplicaciones en tiempo real y de alta concurrencia.

Event Loop Node.js

🔼 Regresar


Módulos

En Node.js, los módulos son bloques de código reutilizables y encapsulados que permiten organizar y separar la funcionalidad de una aplicación en unidades más pequeñas y manejables. Los módulos permiten que el código se escriba de manera modular y fomentan las buenas prácticas de desarrollo, como la reutilización de código y la separación de funcionalidades.

En Node.js, existen dos tipos principales de módulos:

  1. Módulos incorporados: Son módulos que vienen preinstalados con Node.js y están disponibles para su uso sin necesidad de instalar nada adicional. Algunos ejemplos de módulos incorporados son fs (file system) para trabajar con archivos, http para crear servidores web y manejar solicitudes HTTP, path para manipular rutas de archivos, entre otros. Puedes usar estos módulos directamente en tu código sin necesidad de instalarlos.
  2. Módulos personalizados: Son módulos creados por los desarrolladores para encapsular y reutilizar código específico de una aplicación. Los módulos personalizados se crean separando funciones, variables o clases en archivos separados y luego se exportan para que otros archivos puedan importarlos y usarlos. Esto se logra utilizando el sistema de módulos de Node.js.

Sistemas de módulos en Node.js

En Node.js, existen dos tipos principales de sistemas de módulos:

CommonJS (CJS)

Es el sistema de módulos que se utiliza por defecto en Node.js. Utiliza las palabras clave require y module.exports (o exports) para importar y exportar módulos, respectivamente.

  • Importar un módulo (require): Para importar un módulo en Node.js, se utiliza la palabra clave require seguida del nombre del archivo o ruta del módulo que se desea importar. Por ejemplo:
const modulo = require("./ruta/al/modulo.js");
  • Exportar un módulo (module.exports o exports): Para exportar un módulo en Node.js, se utiliza la propiedad module.exports o simplemente exports para asignar valores o funciones al objeto que se exportará. Por ejemplo:
// modulo.js
const funcion1 = () => {
  // ...
};

const funcion2 = () => {
  // ...
};

module.exports = {
  funcion1,
  funcion2,
};

ECMAScript Modules (ESM)

A partir de Node.js versión 13, se agregó soporte para ECMAScript Modules (ESM), que permite utilizar el formato de importación y exportación estándar definido en ECMAScript (el estándar que rige JavaScript).

  • Importar un módulo (import): Para importar un módulo con ESM, se utiliza la palabra clave import seguida del nombre del archivo o ruta del módulo que se desea importar. Por ejemplo:
import modulo from "./ruta/al/modulo.js";
  • Exportar un módulo (export): Para exportar un módulo con ESM, se utiliza la palabra clave export para exportar valores o funciones directamente. Por ejemplo:
// modulo.js
export const funcion1 = () => {
  // ...
};

export const funcion2 = () => {
  // ...
};

Es importante tener en cuenta que para utilizar ESM en Node.js, se debe agregar el atributo "type": "module" en el archivo package.json de tu proyecto, o utilizar la extensión .mjs para los archivos de módulo que utilicen el formato ESM.

Por defecto, CommonJS sigue siendo el sistema de módulos más comúnmente utilizado en proyectos de Node.js, pero el soporte para ESM permite a los desarrolladores aprovechar las características más modernas de importación y exportación estándar de ECMAScript si así lo desean.

🔼 Regresar


NPM

NPM (Node Package Manager) es el administrador de paquetes predeterminado para Node.js, y se utiliza para instalar, actualizar y gestionar paquetes y dependencias de terceros en un proyecto.

Comandos NPM

Para sacarle el máximo provecho a NPM, te dejo un enlace hacia su documentación oficial, de cualquier manera a continuación te enlisto algunos de los comandos más usados:

  • Iniciar un proyecto:
npm init //Con asistente

npm init -y //Sin asistente
  • Instalando paquetes:
npm install [package]

npm install [package]@3.4.12 //Versión específica

npm i [package] //shortcut
  • Actualizar paquetes:
npm update [package]
  • Desinstalar paquetes:
npm uninstall [package]

npm uninstall [package]@3.4.12 //Versión específica

npm un [package] //shortcut

Carpeta node_modules

Cuando ejecutas el comando npm install NPM crea una carpeta llamadanode_modules que es un directorio que se agrega en la raíz de un proyecto Node.js cuando se instalan paquetes o dependencias vía NPM. Esta carpeta es un componente fundamental en la gestión de dependencias en Node.js y contiene todos los paquetes externos y sus dependencias que se utilizan en el proyecto.

La estructura de la carpeta node_modules puede volverse bastante compleja en proyectos grandes con muchas dependencias. Sin embargo, esta estructura es necesaria para asegurar que todas las dependencias estén correctamente organizadas y que no haya conflictos entre diferentes versiones de paquetes.

Es importante tener en cuenta que no es necesario incluir la carpeta node_modules en un sistema de control de versiones (por ejemplo, Git). Esto se debe a que los archivos en node_modules pueden ser fácilmente recreados utilizando el archivo package.json y el comando npm install. Por esta razón, se suele agregar la carpeta node_modules al archivo .gitignore o al sistema de ignorar archivos correspondiente para evitar que se incluyan en el control de versiones y, así, mantener un repositorio más limpio y liviano.

Archivo package.json

Es un archivo de configuración fundamental en proyectos de Node.js. Contiene información sobre el proyecto, sus dependencias, scripts personalizados y otros metadatos relevantes para el proyecto. Este archivo se encuentra en la raíz del directorio del proyecto y es utilizado por NPM para gestionar las dependencias y scripts relacionados con el proyecto.

El archivo package.json es un archivo JSON que contiene una serie de claves y valores. Las claves más comunes incluyen:

  1. name: El nombre del proyecto.
  2. version: La versión actual del proyecto. Sigue un esquema de versionado semántico (pe, 1.0.0).
  3. description: Una breve descripción del proyecto.
  4. main: El archivo principal que se debe ejecutar cuando se importa el proyecto como un módulo.
  5. dependencies: Un objeto que lista las dependencias de producción del proyecto. Contiene los nombres de los paquetes y las versiones requeridas.
  6. devDependencies: Un objeto que lista las dependencias de desarrollo del proyecto. Contiene los nombres de los paquetes y las versiones requeridas para el desarrollo.
  7. scripts: Un objeto que define scripts personalizados que pueden ser ejecutados desde la línea de comandos con el comando npm run.
  8. author: El nombre del autor o autores del proyecto.
  9. license: La licencia bajo la cual se distribuye el proyecto.

Ejemplo

{
  "name": "mi-proyecto",
  "version": "1.0.0",
  "description": "Una aplicación sencilla de ejemplo",
  "main": "app.js",
  "dependencies": {
    "express": "4.17.1",
    "lodash": "4.17.21"
  },
  "devDependencies": {
    "jest": "27.1.1"
  },
  "scripts": {
    "start": "node app.js",
    "test": "jest"
  },
  "author": "Tu Nombre",
  "license": "MIT"
}

Uso y funcionalidad del archivo

El archivo package.json tiene varios propósitos importantes:

  1. Gestión de Dependencias: Permite especificar las dependencias del proyecto en las secciones "dependencies" y "devDependencies". Cuando ejecutas npm install basado en este archivo, NPM descarga y guarda todas las dependencias listadas en la carpeta node_modules del proyecto.
  2. Scripts Personalizados: La sección "scripts" permite definir instrucciones personalizadas que se pueden ejecutar utilizando el comando npm run. Por ejemplo, en el archivo anterior, puedes ejecutar npm run start para iniciar la aplicación y npm run test para ejecutar pruebas con Jest.
  3. Metadatos del Proyecto: Proporciona información sobre el proyecto, como su nombre, versión, descripción, autor, licencia, etc. Esto es útil para otros desarrolladores y para mantener un registro claro del proyecto.

En resumen, el archivo package.json es un componente esencial en proyectos de Node.js y juega un papel crucial en la gestión de dependencias, configuración del proyecto y definición de scripts personalizados. Es una parte integral de la estructura de proyectos de Node.js y permite que NPM administre el proyecto de manera eficiente y precisa.

Archivo package-lock.json

Es un archivo generado automáticamente por NPM cuando se instalan o actualizan las dependencias de un proyecto Node.js. Este archivo tiene un propósito específico y es utilizado para asegurar una instalación reproducible y consistente de las dependencias del proyecto en diferentes entornos.

Funciones y usos del archivo

  1. Versionado preciso de dependencias: Contiene información detallada sobre las versiones exactas de las dependencias que fueron instaladas en el proyecto. Esto incluye las versiones de las dependencias directas e indirectas (dependencias de dependencias) del proyecto. Esto garantiza que las mismas versiones de las dependencias se utilicen en diferentes instalaciones y por diferentes desarrolladores, lo que evita posibles incompatibilidades o sorpresas en la configuración del proyecto.
  2. Bloqueo de versiones: Actúa como una especie de "bloqueo de versiones", lo que significa que cuando alguien más instale las dependencias del proyecto utilizando npm install, NPM instalará exactamente las mismas versiones que se encuentran en el archivo package-lock.json. Esto evita problemas donde se podrían utilizar versiones más recientes de las dependencias, lo que podría llevar a cambios no deseados o errores en el proyecto.
  3. Mayor consistencia y estabilidad: Se asegura una mayor consistencia y estabilidad en la configuración de las dependencias. Esto es especialmente útil en proyectos colaborativos donde diferentes desarrolladores pueden trabajar en diferentes máquinas y sistemas operativos. El archivo package-lock.json garantiza que todos los desarrolladores tengan el mismo entorno de desarrollo.
  4. Rápida instalación de dependencias: Al tener información precisa sobre las versiones de las dependencias en el archivo package-lock.json, NPM puede realizar instalaciones más rápidas y eficientes, ya que no necesita resolver las versiones de las dependencias nuevamente en cada instalación.

Importante: Es fundamental incluir el archivo package-lock.json en el control de versiones del proyecto (por ejemplo, Git) para garantizar que todos los miembros del equipo tengan acceso a la misma configuración de dependencias. Al compartir el proyecto con otros desarrolladores o al implementarlo en diferentes servidores, el archivo package-lock.json asegura que las dependencias se instalen de manera coherente y sin cambios inesperados en las versiones.

Tipos de banderas de dependencias

En Node.js y NPM, existen varias banderas u opciones que se pueden utilizar al momento de instalar dependencias. Estas banderas proporcionan diferentes funcionalidades y configuraciones para la instalación de paquetes y la gestión de dependencias. A continuación, se enumeran algunas de las banderas más comunes y su propósito:

  1. --save (-S): Esta bandera se usa para guardar la dependencia en la sección "dependencies" del archivo package.json. Por ejemplo:

    npm install express --save
  2. --save-dev (-D): Esta bandera se usa para guardar la dependencia en la sección "devDependencies" del archivo package.json. Por ejemplo:

    npm install jest --save-dev
  3. --global (-g): Esta bandera se usa para instalar la dependencia de forma global en el sistema, lo que la hace accesible para todos los proyectos. Por ejemplo:

    npm install nodemon -g
  4. --production: Esta bandera se usa para instalar solo las dependencias de producción y omitir las dependencias de desarrollo. Por ejemplo:

    npm install --production
  5. --no-save: Esta bandera se usa para evitar que NPM agregue la dependencia al archivo package.json. Es útil si solo quieres instalar una dependencia temporalmente sin guardarla en el proyecto. Por ejemplo:

    npm install lodash --no-save
  6. --legacy-peer-deps: Esta bandera se usa para permitir que NPM instale paquetes que presenten advertencias de dependencias obsoletas. Es útil si trabajas con paquetes que aún no han actualizado sus dependencias.

  7. --prefer-offline: Esta bandera se usa para preferir las versiones descargadas de los paquetes, incluso si están desactualizadas, en lugar de descargar las últimas versiones desde el registro.

  8. --ignore-scripts: Esta bandera se usa para evitar la ejecución de scripts definidos en los paquetes durante la instalación. Puede ser útil si no deseas que los scripts personalizados de los paquetes se ejecuten al instalarlos.

  9. --audit: Esta bandera se usa para verificar si las dependencias tienen vulnerabilidades conocidas y proporciona información sobre cómo solucionarlas.

Estas son solo algunas de las banderas más comunes utilizadas en la instalación de dependencias con NPM. Pueden ser muy útiles para controlar cómo se instalan y administran las dependencias en proyectos de Node.js. Puedes obtener más información sobre todas las opciones disponibles ejecutando npm help install desde la línea de comandos.

Comando npx

El comando npx es una herramienta incluida en NPM a partir de la versión 5.2.0. Su función principal es permitir la ejecución de paquetes Node.js temporales o instalados globalmente sin la necesidad de instalarlos en tu sistema o proyecto de manera permanente.

Funciones y usos principales

  1. Ejecución de paquetes temporales: npx se usa para ejecutar paquetes temporales sin tener que instalarlos previamente. Por ejemplo, si hay un paquete disponible en el registro de NPM que solo necesitas usar una vez o de forma esporádica, puedes usar npx para ejecutarlo sin la necesidad de instalarlo globalmente o agregarlo a tu proyecto.

    npx create-react-app my-app

    En este ejemplo, npx ejecuta el paquete create-react-app temporalmente para crear una nueva aplicación de React llamada my-app sin necesidad de instalar el paquete globalmente en el sistema.

  2. Ejecución de comandos de paquetes instalados globalmente: npx permite ejecutar comandos de paquetes que están instalados globalmente en tu sistema, incluso si no están disponibles en el directorio actual. Esto puede ser útil si necesitas acceder a una utilidad de línea de comandos que has instalado globalmente.

    npx nodemon app.js

    En este ejemplo, npx ejecuta el paquete nodemon globalmente para monitorear y reiniciar automáticamente la aplicación app.js.

  3. Seleccionar versiones específicas de paquetes: npx permite especificar versiones específicas de un paquete para ejecutar un comando. Esto puede ser útil para probar comandos en diferentes versiones de paquetes sin tener que instalar varias versiones de forma permanente.

    npx mocha@6 test.js

    En este ejemplo, npx ejecuta el paquete mocha en su versión 6 para ejecutar las pruebas definidas en test.js.

En resumen, npx es una herramienta poderosa que facilita la ejecución de paquetes temporales o instalados globalmente sin la necesidad de instalarlos permanentemente en tu sistema o proyecto. Puedes usar npx para probar nuevas herramientas, ejecutar comandos de paquetes globales o probar versiones específicas de paquetes de forma temporal y eficiente.

Como puedes ver NPM y todo su ecosistema de elementos, lo hacen una herramienta poderosa que facilita la gestión de dependencias y la instalación de paquetes, lo que permite que los desarrolladores aprovechen las soluciones existentes y la comunidad de código abierto para acelerar el desarrollo de sus proyectos.

🔼 Regresar


Aprende más

Si estás interesado en aprender más sobre JavaScript en el lado del servidor, a través de su poderoso entorno de programación backend, Node.js, no te pierdas mis cursos totalmente gratuitos en mi canal de YouTube.

¡¡¡Accede ya!!!

Ver Cursos

🔼 Regresar