Search lands in PR-5.1 (Pagefind).

Explanation Beginner

Chapter 4 Updated

Modules, Exports & Require

CommonJS basics — `require`, `module.exports`, the `index.js` barrel, and how ESM differs.

  • Full 15m
  • Revision 4m
  • Flow 2m

The Problem — One File Is Not Enough

In the previous chapter, we wrote all our code in a single app.js. That works for tiny experiments, but imagine building an e-commerce site — authentication, product listings, shopping cart, payment processing, email notifications. Stuffing all of that into one file would be chaos.

This is why Node.js uses a module system. Every .js file in Node.js is automatically its own module. Each file has its own private scope — variables and functions defined in one file are invisible to other files by default.

Module isolation — each file is a fortress
┌──────────── sum.js ────────────┐     ✗      ┌──────────── app.js ─────────┐
│ function calculateSum() {...}  │ ─────────→ │ calculateSum(5, 10)         │
│ let x = "hello"                │  (blocked) │ // ReferenceError!          │
│   PRIVATE by default           │            │ // calculateSum is not      │
│   Cannot be accessed outside   │            │ //   defined                │
└────────────────────────────────┘            └─────────────────────────────┘
 
                         ↓ SOLUTION ↓
              1. Export from sum.js   — module.exports
              2. Import in app.js     — require("./sum")

The Bridge — require() & module.exports

Node.js gives you two tools to connect modules together: module.exports (the sender) and require() (the receiver). Think of it like a delivery service — one module packages what it wants to share, and another module receives the package.

Let’s walk through the complete flow with code.

sum.js — exporting a function
// sum.js
function calculateSum(a, b) {
  let sum = a + b;
  console.log(sum);
}
 
// Make it available to the outside world
module.exports = calculateSum;
app.js — importing and using it
// app.js
const calculateSum = require("./sum.js");
 
calculateSum(5, 10); // Output: 15

Exporting Multiple Things

What if you need to export more than one thing from a module — say, a variable and a function? You wrap them in an object:

sum.js — exporting multiple items
let x = "export in React exports in Node";
 
function calculateSum(a, b) {
  console.log(a + b);
}
 
// Export as an object
module.exports = { x, calculateSum };
app.js — using destructuring to import
// Destructuring — the clean, professional way
const { x, calculateSum } = require("./sum.js");
 
calculateSum(5, 10); // 15
console.log(x);      // "export in React exports in Node"

Destructuring is a JavaScript feature that lets you unpack properties from an object into separate variables. Most professional Node.js developers use this pattern daily — you’ll see it everywhere in production codebases.

Scaling Up — Folders, Nested Modules & index.js

As your project grows, you’ll want to group related modules into folders. For example, a calculate/ folder might contain sum.js and multiply.js. You can import from subfolders using relative paths:

Importing from subfolders
const { calculateSum } = require("./calculate/sum");
const { calculateMultiply } = require("./calculate/multiply");

But there’s an even cleaner pattern: create an index.js file inside the folder. This file acts as a “receptionist” — it gathers all exports from sibling files and re-exports them as a single package.

calculate/index.js — the barrel file
const { calculateMultiply } = require("./multiply");
const { calculateSum } = require("./sum");
 
module.exports = { calculateMultiply, calculateSum };
app.js — one clean import from the folder
// Node.js automatically looks for index.js inside the folder!
const { calculateSum, calculateMultiply } = require("./calculate");
 
calculateSum(5, 10);       // 15
calculateMultiply(5, 10);  // 50
Project structure with nested modules
project/
├─ app.js             ← entry point, requires ./calculate
├─ data.json          ← JSON files can be required too!
├─ xyz.js
└─ calculate/         ← folder acts as a module
   ├─ index.js        ← barrel file (auto-loaded by Node)
   ├─ sum.js          ← exports calculateSum
   └─ multiply.js     ← exports calculateMultiply

The Two Worlds — CommonJS vs ES Modules

Everything we’ve learned so far uses CommonJS (CJS) — the original module system built into Node.js. But there’s a newer system called ES Modules (ESM) that’s becoming the future standard. You need to know both.

FeatureCommonJS (CJS)ES Modules (ESM)
Syntaxrequire() / module.exportsimport / export
File extension.js (default).mjs or .js with config
LoadingSynchronousAsynchronous
Strict modeNon-strict (sloppy mode)Always strict mode
Default inNode.js (traditional)Browsers, React, Angular
EraOlder, establishedNewer, the future
How to enableDefault — works out of the boxAdd "type": "module" in package.json

Here’s what ES Module syntax looks like compared to CommonJS.

ES Modules — sum.js
// ESM: use the 'export' keyword directly
export let x = "export in React exports in Node";
 
export function calculateSum(a, b) {
  console.log(a + b);
}
ES Modules — app.js
// ESM: use 'import' instead of 'require'
import { calculateSum, x } from "./sum.js";
 
calculateSum(5, 10); // 15
console.log(x);

To enable ES Modules in your project, create a package.json file and add "type": "module". The industry is gradually shifting toward ESM — most new frameworks and libraries already use it.

Episode 04 — At a Glance

ConceptKey detail
ModuleA file or folder with private JS code — nothing leaks by default.
require()Built-in function to import another module’s exports.
module.exportsEmpty object by default — assign to it to make things public.
Export one thingmodule.exports = calculateSum;
Export multiplemodule.exports = { x, calculateSum };
Destructured importconst { x, calculateSum } = require("./sum");
Omit .jsNode.js auto-resolves file extensions.
index.js patternBarrel file in a folder — lets you require("./folder").
Require JSONconst data = require("./data.json");
Built-in modulesrequire('node:util'), require('node:fs'), etc.
CommonJS (CJS)require/exports, synchronous, non-strict, Node.js default.
ES Modules (ESM)import/export, async, strict mode, the future standard.
Enable ESMAdd "type": "module" to package.json.

Comments

Comments are disabled in this environment. Set PUBLIC_GISCUS_REPO, PUBLIC_GISCUS_REPO_ID, and PUBLIC_GISCUS_CATEGORY_ID to enable.