Un proyecto de Node.js crece rápido: rutas, controladores, utilidades, configuración y decenas de paquetes de terceros. Saber cómo funcionan los módulos y npm es lo que mantiene ese crecimiento ordenado. Además, Node.js tiene dos sistemas de módulos conviviendo, y mezclarlos sin entenderlos genera errores desconcertantes. En el libro Node.js desde cero lo vemos en el capítulo de módulos, paquetes y npm; acá tenés lo esencial.
ES Modules vs CommonJS
| CommonJS (CJS) | ES Modules (ESM) | |
|---|---|---|
| Sintaxis | require() / module.exports | import / export |
| Origen | El sistema histórico de Node.js | El estándar de JavaScript, igual que en el navegador |
| Carga | Síncrona | Asíncrona; permite await en el nivel superior |
| Cómo se activa | Por defecto en archivos .js y siempre en .cjs | "type": "module" en package.json, o archivos .mjs |
__dirname | Disponible | Usá import.meta.dirname (Node.js 20.11 o superior) |
Para proyectos nuevos, la recomendación es ESM: es el estándar del lenguaje y el que adoptan cada vez más paquetes. Vas a seguir viendo CommonJS en código existente y en muchos tutoriales, así que conviene reconocerlo.
Crear tus propios módulos
// Exportaciones con nombre: se importan con el mismo nombre
export const IVA = 0.21;
export function conIva(monto) {
return Math.round(monto * (1 + IVA) * 100) / 100;
}
// Exportación por defecto: una por módulo
export default function formatear(monto, moneda = 'ARS') {
return new Intl.NumberFormat('es-AR', { style: 'currency', currency: moneda }).format(monto);
}
import formatear, { conIva, IVA } from './utils/precio.js'; // ¡la extensión es obligatoria en ESM!
import { readFile } from 'node:fs/promises'; // módulos nativos con el prefijo node:
console.log(`IVA: ${IVA * 100}%`);
console.log(formatear(conIva(1000))); // $ 1.210,00
// await en el nivel superior: solo en ES Modules
const config = JSON.parse(await readFile(new URL('../config.json', import.meta.url), 'utf8'));
Y para comparar, el mismo módulo en CommonJS:
const IVA = 0.21;
function conIva(monto) {
return Math.round(monto * (1 + IVA) * 100) / 100;
}
module.exports = { IVA, conIva };
// en otro archivo: const { conIva } = require('./precio.cjs');
package.json, paso a paso
$ npm init -y # crea package.json
$ npm install express # dependencia de la aplicación
$ npm install -D eslint # dependencia solo de desarrollo
{
"name": "api-blog",
"version": "1.0.0",
"type": "module",
"main": "src/server.js",
"engines": { "node": ">=20" },
"scripts": {
"dev": "node --watch --env-file=.env src/server.js",
"start": "node src/server.js",
"test": "node --test",
"lint": "eslint ."
},
"dependencies": {
"express": "^5.1.0"
},
"devDependencies": {
"eslint": "^9.0.0"
}
}
dependencies: lo que la aplicación necesita para funcionar.devDependencies: herramientas de desarrollo (linters, tests). En producción se omiten connpm ci --omit=dev.package-lock.json: registra la versión exacta de cada paquete instalado. Subilo al repositorio: garantiza que todos instalen exactamente lo mismo.node_modules/: nunca al repositorio; va en.gitignore.
Versiones: qué significa ^ y ~
npm usa versionado semántico: MAYOR.MENOR.PARCHE. Un cambio de MAYOR puede romper la
compatibilidad; MENOR añade funciones; PARCHE corrige errores.
| Rango | Acepta | Ejemplo con 4.21.0 |
|---|---|---|
^4.21.0 | Cambios menores y parches | 4.21.1, 4.22.0… pero no 5.0.0 |
~4.21.0 | Solo parches | 4.21.1, 4.21.9… pero no 4.22.0 |
4.21.0 | Exactamente esa versión | 4.21.0 |
Scripts de npm útiles
Los scripts del package.json anterior aprovechan funciones que Node.js ya trae de serie:
npm run dev:--watchreinicia el servidor al guardar cambios y--env-file=.envcarga las variables de entorno, sin instalar nada extra.npm test: ejecuta los tests con el test runner integradonode:test.npm start: el comando que usan los servicios de hosting para arrancar la aplicación.
Errores comunes
Cannot use import statement outside a module: falta"type": "module"o el archivo no es.mjs.ERR_MODULE_NOT_FOUNDcon un archivo propio: en ESM hay que escribir la extensión (./precio.js, no./precio).__dirname is not defined: en ESM usáimport.meta.dirname.- Instalar paquetes sin revisarlos: cada dependencia es código de terceros en tu
servidor. Revisá su mantenimiento y ejecutá
npm auditcon regularidad.
Organiza tus proyectos como un profesional
El libro Node.js desde cero dedica un capítulo a módulos, paquetes y npm, la base para estructurar los tres proyectos completos del libro.
Comprar en Amazon