BuildBot

Node Essentials

Modules: ESM vs CommonJS

Lesson 1 of 5

What you'll learn

  • Distinguish CommonJS (require) from ES Modules (import)
  • See that one file is one module with its own private scope
  • Understand how a name maps to a resolved module

In Node, one file is one module. Everything you declare in a file is private to that file unless you explicitly export it. Other files reach in only through what you expose, and pull it out by name.

There are two systems. CommonJS is the original Node format — synchronous, value-based:

// math.js
function add(a, b) { return a + b; }
module.exports = { add };

// app.js
const { add } = require("./math.js");
console.log(add(2, 3));

ES Modules (ESM) is the standard JavaScript format, used when the file is .mjs or the package has "type": "module":

// math.mjs
export function add(a, b) { return a + b; }

// app.mjs
import { add } from "./math.mjs";
console.log(add(2, 3));

Loading is just resolve + cache

When you require("./math.js"), Node resolves the string to a real file, runs that file once, and caches its exports. Ask for the same name again and you get the cached object back — modules are singletons. That single-evaluation rule is why module-level setup code runs exactly once.

Pick one per package

Don't mix require and import haphazardly. A package's "type" field decides the default. ESM can load CommonJS, but CommonJS can't require an ESM file — that asymmetry trips up beginners constantly.

A module system is, at its core, a registry: a name in, a set of exports out, evaluated once. The challenge builds exactly that.

A tiny module registry

Run it. define() registers a module factory by name; require() runs it once, caches the exports, and resolves later calls from cache.

Loading editor…

Next: why Node can serve thousands of requests on a single thread.

Sign in to save your progress across devices.