CodingNic

Node.js Fundamentals

Module Systems: CommonJS and ES Modules

Node.js Fundamentals 12 min read

Module Systems: CommonJS and ES Modules

Objectives

By the end of this lesson, you should be able to:

  • Write and use a module in CommonJS (require/module.exports)
  • Write and use a module in ES Modules (import/export)
  • Explain how Node.js decides which system a file uses, and why mixing them incorrectly fails

💡 Why this matters: Module 2’s import/export lesson covered the JavaScript language syntax. This lesson covers Node.js’s two actual, real-world module systems, and the older one, CommonJS, is still extremely common in existing Node.js code and packages.

⚠️ A note on verification: every snippet and output in this lesson was actually run with Node.js.

CommonJS

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

module.exports = { add };
javascript
// main.js
const { add } = require('./math.js');
console.log(add(2, 3));
text
5

CommonJS is Node’s original module system, module.exports exposes values from a file, require() loads them in another file. require() is synchronous, it loads and returns a module’s exports immediately, blocking until it’s done, this is part of why it can’t directly load an ES Module, covered below.

ES Modules

javascript
// math.js
export function add(a, b) {
  return a + b;
}
javascript
// main.js
import { add } from './math.js';
console.log(add(2, 3));
text
5

This is the same import/export syntax from Module 2, now actually running in Node.js. ES Modules are the standardized JavaScript module system, the same one browsers use natively.

How Node.js Decides Which System to Use

Node.js needs to know, per file, which system it’s using. Two ways to declare it: a .mjs file extension always means ES Modules, a .cjs extension always means CommonJS, regardless of anything else. For plain .js files, Node checks the nearest package.json’s "type" field, "type": "module" treats every .js file in that project as an ES Module, no "type" field (or "type": "commonjs") treats them as CommonJS, Node’s original default.

Mixing Them Incorrectly Fails

javascript
// package.json has "type": "module"
// uses-require.js
const fs = require('fs');
console.log(fs);
text
file:///tmp/node103/mixed-demo2/uses-require.js:1
const fs = require('fs');
           ^

ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and 'package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.

Once a file is being treated as an ES Module, require genuinely doesn’t exist in it, require is a CommonJS-specific global, not part of the ES Module system at all. Node’s error message here is unusually direct about the fix: rename the file to .cjs, or use import instead.

Choosing One for a Project

Mixing both systems in the same project is possible but adds real complexity. Most new Node.js projects, including the Express projects built starting in Module 5, pick one system, commonly ES Modules for new code, matching the syntax already familiar from Module 2, and stick with it consistently.

Try It

  1. Create a small CommonJS module with module.exports, and use it from another file with require().
  2. Create a small ES Module with export, set "type": "module" in package.json, and use it from another file with import.
  3. Try using require() inside a file governed by "type": "module", and read the resulting error carefully.
  4. Explain, in your own words, how Node.js decides whether a given .js file is CommonJS or an ES Module.

Recap

  • CommonJS uses require() and module.exports, Node’s original, still widely used module system.
  • ES Modules use import/export, the standardized system, the same one browsers use.
  • A .mjs/.cjs extension, or a package.json’s "type" field, determines which system a .js file uses, and require simply doesn’t exist inside a file being treated as an ES Module.

Next lesson: environment variables, configuring a program without hardcoding values.