libro / blog / modulos-npm-nodejs

// módulos

Módulos en Node.js: ES Modules, CommonJS y npm

Gustavo Arias8 min de lectura

TL;DR Para proyectos nuevos usa ES Modules (import/export) con "type": "module" en package.json. Instala dependencias con npm, guarda el package-lock.json en el repositorio, usa npm ci en servidores y define tus comandos habituales en la sección scripts.

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)
Sintaxisrequire() / module.exportsimport / export
OrigenEl sistema histórico de Node.jsEl estándar de JavaScript, igual que en el navegador
CargaSíncronaAsíncrona; permite await en el nivel superior
Cómo se activaPor defecto en archivos .js y siempre en .cjs"type": "module" en package.json, o archivos .mjs
__dirnameDisponibleUsá 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"
  }
}

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.

RangoAceptaEjemplo con 4.21.0
^4.21.0Cambios menores y parches4.21.1, 4.22.0… pero no 5.0.0
~4.21.0Solo parches4.21.1, 4.21.9… pero no 4.22.0
4.21.0Exactamente esa versión4.21.0

Scripts de npm útiles

Los scripts del package.json anterior aprovechan funciones que Node.js ya trae de serie:

Errores comunes

Referencia: ECMAScript modules en la documentación de Node.js y la documentación de npm.

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