Node.js

Node

Tutorial Node: Módulos en node

Aprende los tipos de módulos en Node.js, diferencias entre CommonJS y ES Modules, y cómo usarlos para crear aplicaciones escalables y mantenibles.

Aprende Node y certifícate

Qué son los módulos en Node.js

Los módulos en Node.js son unidades de código reutilizable que permiten organizar y estructurar las aplicaciones de manera eficiente. Un módulo puede contener funciones, objetos, clases o cualquier código JavaScript que se puede exportar para ser utilizado en otros archivos. Esta modularidad es fundamental para crear aplicaciones escalables y mantenibles.

En Node.js, cada archivo JavaScript se considera automáticamente un módulo independiente. Esto significa que las variables, funciones y clases definidas en un archivo no son accesibles desde otros archivos a menos que se exporten explícitamente. Esta característica proporciona un encapsulamiento natural que evita la contaminación del espacio de nombres global.

Tipos de módulos en Node.js

Node.js trabaja con tres tipos principales de módulos:

  • Módulos nativos: Son módulos integrados en Node.js que proporcionan funcionalidades básicas del sistema operativo y del entorno de ejecución.
const fs = require('fs');
const path = require('path');
const http = require('http');
  • Módulos locales: Son archivos JavaScript que creamos en nuestro proyecto para organizar el código en diferentes funcionalidades.
// archivo: calculadora.js
function sumar(a, b) {
    return a + b;
}

function restar(a, b) {
    return a - b;
}

module.exports = { sumar, restar };
  • Módulos de terceros: Son paquetes desarrollados por la comunidad que se instalan a través de npm y proporcionan funcionalidades adicionales.
// Después de instalar con: npm install lodash
const _ = require('lodash');

Funcionamiento del sistema de módulos

Cuando Node.js encuentra una declaración de importación, ejecuta un proceso de resolución para localizar el módulo solicitado. Este proceso sigue un orden específico de búsqueda:

1. Módulos nativos: Node.js primero verifica si el nombre corresponde a un módulo nativo.

2. Rutas relativas y absolutas: Si el nombre comienza con ./, ../ o /, Node.js busca el archivo en la ruta especificada.

const miModulo = require('./utils/helpers');
const config = require('../config/database');

3. Carpeta node_modules: Para nombres que no son rutas, Node.js busca en la carpeta node_modules del directorio actual y en las carpetas padre.

Exportación e importación básica

La exportación de funcionalidades se realiza mediante el objeto module.exports o la propiedad exports. Ambos métodos permiten hacer disponible el código para otros módulos:

// archivo: matematicas.js
function multiplicar(a, b) {
    return a * b;
}

function dividir(a, b) {
    if (b === 0) {
        throw new Error('División por cero no permitida');
    }
    return a / b;
}

// Exportación usando module.exports
module.exports = {
    multiplicar,
    dividir
};

Para importar y utilizar este módulo en otro archivo:

// archivo: app.js
const matematicas = require('./matematicas');

console.log(matematicas.multiplicar(5, 3)); // 15
console.log(matematicas.dividir(10, 2)); // 5

También es posible importar funciones específicas mediante desestructuración:

const { multiplicar, dividir } = require('./matematicas');

console.log(multiplicar(4, 7)); // 28
console.log(dividir(20, 4)); // 5

Cache de módulos

Node.js implementa un sistema de cache para los módulos importados. Cuando un módulo se requiere por primera vez, Node.js lo ejecuta y almacena el resultado en cache. Las siguientes importaciones del mismo módulo devuelven la instancia cacheada sin volver a ejecutar el código:

// archivo: contador.js
let contador = 0;

function incrementar() {
    contador++;
    return contador;
}

function obtenerValor() {
    return contador;
}

module.exports = { incrementar, obtenerValor };
// archivo: app.js
const contador1 = require('./contador');
const contador2 = require('./contador');

console.log(contador1.incrementar()); // 1
console.log(contador2.obtenerValor()); // 1 (mismo estado compartido)
console.log(contador1 === contador2); // true (misma instancia)

Esta característica es importante para entender que los módulos son singleton por defecto, lo que significa que mantienen su estado entre diferentes importaciones en la misma aplicación.

CommonJS vs ES Modules

Node.js soporta dos sistemas de módulos diferentes que han evolucionado a lo largo del tiempo. CommonJS fue el sistema original de Node.js, mientras que ES Modules representa el estándar moderno de JavaScript que se ha adoptado tanto en navegadores como en Node.js.

CommonJS: El sistema tradicional

CommonJS es el sistema de módulos que Node.js ha utilizado desde sus inicios. Se basa en las funciones require() para importar módulos y module.exports o exports para exportarlos. Este sistema funciona de manera síncrona y carga los módulos en tiempo de ejecución.

Las características principales de CommonJS incluyen:

  • Carga síncrona: Los módulos se cargan completamente antes de continuar con la ejecución.
  • Carga dinámica: Es posible usar require() dentro de condicionales o funciones.
  • Compatibilidad total: Funciona en todas las versiones de Node.js sin configuración adicional.
// Exportación en CommonJS
const configuracion = {
    puerto: 3000,
    baseDatos: 'mongodb://localhost:27017'
};

function conectar() {
    console.log('Conectando a la base de datos...');
}

module.exports = {
    configuracion,
    conectar
};
// Importación en CommonJS
const { configuracion, conectar } = require('./config');

// Importación condicional (permitida en CommonJS)
if (process.env.NODE_ENV === 'development') {
    const debug = require('./debug-tools');
    debug.activar();
}

ES Modules: El estándar moderno

ES Modules (también conocidos como ESM) representan el sistema de módulos estándar de JavaScript definido en ECMAScript 2015. Node.js introdujo soporte experimental para ES Modules en la versión 12 y lo estabilizó completamente en versiones posteriores.

Las características distintivas de ES Modules son:

  • Carga asíncrona: Los módulos se pueden cargar de forma asíncrona.
  • Análisis estático: Las importaciones y exportaciones se determinan en tiempo de compilación.
  • Sintaxis más limpia: Utiliza las palabras clave import y export.
// Exportación en ES Modules
export const configuracion = {
    puerto: 3000,
    baseDatos: 'mongodb://localhost:27017'
};

export function conectar() {
    console.log('Conectando a la base de datos...');
}

// Exportación por defecto
export default class Servidor {
    constructor(puerto) {
        this.puerto = puerto;
    }
    
    iniciar() {
        console.log(`Servidor iniciado en puerto ${this.puerto}`);
    }
}
// Importación en ES Modules
import { configuracion, conectar } from './config.js';
import Servidor from './config.js';

// Importación de todo el módulo
import * as config from './config.js';

Configuración para usar ES Modules

Para utilizar ES Modules en Node.js, necesitas configurar tu proyecto de una de estas formas:

Opción 1: Usar extensión .mjs

// archivo: servidor.mjs
import { createServer } from 'http';

const servidor = createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hola desde ES Modules');
});

servidor.listen(3000);

Opción 2: Configurar package.json

{
    "name": "mi-proyecto",
    "version": "1.0.0",
    "type": "module",
    "main": "index.js"
}

Con esta configuración, todos los archivos .js del proyecto se interpretan como ES Modules por defecto.

Diferencias prácticas en el uso

La sintaxis y el comportamiento difieren significativamente entre ambos sistemas:

Importaciones dinámicas:

// CommonJS - Permitido
function cargarModulo(nombre) {
    return require(`./${nombre}`);
}

// ES Modules - Requiere import() dinámico
async function cargarModulo(nombre) {
    const modulo = await import(`./${nombre}.js`);
    return modulo;
}

Variables globales del módulo:

// CommonJS - Disponibles automáticamente
console.log(__dirname); // Ruta del directorio actual
console.log(__filename); // Ruta del archivo actual

// ES Modules - Requieren import.meta
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

Exportaciones condicionales:

// CommonJS - Funciona sin problemas
if (process.env.NODE_ENV === 'production') {
    module.exports = require('./produccion');
} else {
    module.exports = require('./desarrollo');
}

// ES Modules - No permitido (debe ser estático)
// Esto causará un error de sintaxis
if (process.env.NODE_ENV === 'production') {
    export * from './produccion.js'; // ❌ Error
}

Interoperabilidad entre sistemas

Node.js permite cierta interoperabilidad entre CommonJS y ES Modules, aunque con limitaciones:

Importar CommonJS desde ES Modules:

// archivo-commonjs.js (CommonJS)
module.exports = {
    mensaje: 'Hola desde CommonJS'
};

// archivo-esm.js (ES Modules)
import datos from './archivo-commonjs.js';
console.log(datos.mensaje); // Funciona correctamente

Importar ES Modules desde CommonJS:

// archivo-esm.js (ES Modules)
export const mensaje = 'Hola desde ES Modules';

// archivo-commonjs.js (CommonJS)
// require('./archivo-esm.js'); // ❌ No funciona directamente

// Solución usando import() dinámico
async function cargarESM() {
    const { mensaje } = await import('./archivo-esm.js');
    console.log(mensaje);
}

cargarESM();

Recomendaciones de uso

Para proyectos nuevos en Node.js, se recomienda utilizar ES Modules cuando sea posible, ya que representan el futuro del ecosistema JavaScript. Sin embargo, CommonJS sigue siendo válido y necesario en muchos contextos:

  • Usa ES Modules para proyectos nuevos que no dependan de paquetes legacy.
  • Mantén CommonJS en proyectos existentes donde la migración sea compleja.
  • Considera la compatibilidad con las dependencias de tu proyecto antes de elegir.

La elección entre ambos sistemas dependerá de las necesidades específicas de tu proyecto y del ecosistema de paquetes que utilices.

Aprende Node online

Otras lecciones de Node

Accede a todas las lecciones de Node y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Node y certifícate

Ejercicios de programación de Node

Evalúa tus conocimientos de esta lección Módulos en node con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.