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.
┌──────────── 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
function calculateSum(a, b) {
let sum = a + b;
console.log(sum);
}
// Make it available to the outside world
module.exports = calculateSum;// app.js
const calculateSum = require("./sum.js");
calculateSum(5, 10); // Output: 15Exporting 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:
let x = "export in React exports in Node";
function calculateSum(a, b) {
console.log(a + b);
}
// Export as an object
module.exports = { x, calculateSum };// 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:
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.
const { calculateMultiply } = require("./multiply");
const { calculateSum } = require("./sum");
module.exports = { calculateMultiply, calculateSum };// Node.js automatically looks for index.js inside the folder!
const { calculateSum, calculateMultiply } = require("./calculate");
calculateSum(5, 10); // 15
calculateMultiply(5, 10); // 50project/
├─ 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 calculateMultiplyThe 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.
| Feature | CommonJS (CJS) | ES Modules (ESM) |
|---|---|---|
| Syntax | require() / module.exports | import / export |
| File extension | .js (default) | .mjs or .js with config |
| Loading | Synchronous | Asynchronous |
| Strict mode | Non-strict (sloppy mode) | Always strict mode |
| Default in | Node.js (traditional) | Browsers, React, Angular |
| Era | Older, established | Newer, the future |
| How to enable | Default — works out of the box | Add "type": "module" in package.json |
Here’s what ES Module syntax looks like compared to CommonJS.
// ESM: use the 'export' keyword directly
export let x = "export in React exports in Node";
export function calculateSum(a, b) {
console.log(a + b);
}// 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
| Concept | Key detail |
|---|---|
| Module | A file or folder with private JS code — nothing leaks by default. |
require() | Built-in function to import another module’s exports. |
module.exports | Empty object by default — assign to it to make things public. |
| Export one thing | module.exports = calculateSum; |
| Export multiple | module.exports = { x, calculateSum }; |
| Destructured import | const { x, calculateSum } = require("./sum"); |
Omit .js | Node.js auto-resolves file extensions. |
index.js pattern | Barrel file in a folder — lets you require("./folder"). |
| Require JSON | const data = require("./data.json"); |
| Built-in modules | require('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 ESM | Add "type": "module" to package.json. |
Comments
Comments are disabled in this environment. Set
PUBLIC_GISCUS_REPO,PUBLIC_GISCUS_REPO_ID, andPUBLIC_GISCUS_CATEGORY_IDto enable.