← Node & Express

Fundamentos de Node

¿Cuál es la diferencia entre CommonJS y ES Modules en Node.js, y cómo activas cada uno?

Ver respuesta — intenta responderla en voz alta primero

CommonJS (CJS) es el sistema de módulos histórico de Node: usa require() y module.exports, y carga los módulos de forma síncrona. ES Modules (ESM) es el estándar oficial de JavaScript: usa import y export, y su carga es asíncrona. Activas ESM usando la extensión .mjs o poniendo "type": "module" en el package.json. En proyectos nuevos se recomienda ESM.

Un "módulo" es simplemente un archivo que exporta cosas (funciones, objetos, valores) para que otros archivos las usen. Node tiene dos sistemas para esto:

CommonJS (CJS) — el original de Node:

  • Para exportar: module.exports o exports.
  • Para importar: require().
  • La carga es síncrona: cuando haces require, Node lee y ejecuta ese archivo en ese instante, deteniendo la ejecución hasta terminar. Esto funciona bien porque los archivos están en disco local.
  • require es dinámico: puedes llamarlo dentro de un if o a mitad de una función.

ES Modules (ESM) — el estándar moderno de JavaScript (el mismo del navegador):

  • Para exportar: export y export default.
  • Para importar: import.
  • La carga es asíncrona: el motor analiza primero todas las importaciones antes de ejecutar. Por eso import (la forma estática) debe ir arriba del archivo, no dentro de un if. (Existe import() dinámico, que sí devuelve una promesa y puede usarse en cualquier parte.)
  • Es el futuro: soporta top-level await y es compatible con navegadores.

Cómo activar ESM en Node:

  1. Usar la extensión .mjs en tus archivos, o
  2. Poner "type": "module" en tu package.json (así todos los .js se tratan como ESM). En ese caso, si necesitas un archivo CommonJS puntual, usas la extensión .cjs.

¿Cuándo usar cada uno?

  • ESM: proyectos nuevos, código que compartes con el frontend, cuando quieres top-level await. Es la dirección recomendada hoy.
  • CommonJS: proyectos o librerías antiguas, o cuando dependes de paquetes que aún no ofrecen versión ESM. Sigue siendo totalmente válido y muy común.
// ===== CommonJS =====

// archivo: matematicas.cjs
function sumar(a, b) {
  return a + b;
}
module.exports = { sumar }; // exportamos un objeto con la funcion

// archivo: app.cjs
const { sumar } = require("./matematicas.cjs"); // require es sincrono
console.log(sumar(2, 3)); // 5
// ===== ES Modules =====

// archivo: matematicas.mjs
export function sumar(a, b) {
  return a + b;
}
export default function restar(a, b) {
  return a - b;
}

// archivo: app.mjs
import restar, { sumar } from "./matematicas.mjs"; // import va arriba del archivo
console.log(sumar(2, 3)); // 5
console.log(restar(5, 2)); // 3
// Alternativa: activar ESM para todos los .js con package.json
{
  "name": "mi-proyecto",
  "type": "module"
}

Mezclar sintaxis sin darse cuenta: usar require() en un archivo que Node trata como ESM (porque tiene "type": "module"), lo que produce el error require is not defined in ES module scope. O al revés, usar import en un archivo CommonJS. Otro error frecuente es olvidar la extensión del archivo en los import de ESM: en Node, import { sumar } from "./matematicas" puede fallar; debes escribir "./matematicas.mjs".

"CommonJS es el sistema histórico de Node: usa require y module.exports, y carga los módulos de forma síncrona, así que require puede ir dentro de un if o una función. ES Modules es el estándar oficial de JavaScript: usa import y export, su carga es asíncrona y los imports estáticos deben ir arriba del archivo. Activo ESM con la extensión .mjs o poniendo type module en el package.json, y en ese caso uso .cjs para los archivos CommonJS puntuales. Para proyectos nuevos prefiero ESM porque es el estándar, se lleva bien con el frontend y soporta top-level await; CommonJS lo mantengo cuando trabajo con código o dependencias antiguas."

Reto rápido

Tienes un package.json con "type": "module" y este archivo index.js. ¿Qué pasa al ejecutarlo?

const fs = require("fs");
console.log("hola");
Ver respuesta

Falla con un error parecido a: ReferenceError: require is not defined in ES module scope.

Como el package.json tiene "type": "module", Node trata el index.js como ESM, y en ESM no existe require. La solución es usar import fs from "fs"; (o import fs from "node:fs";), o bien renombrar el archivo a index.cjs si de verdad quieres usar CommonJS.