CommonJS Modules (require/exports)
As your Node.js applications grow, you'll need to split code into separate files. Node.js's module system lets you organize code into reusable pieces. In this lesson, you'll learn CommonJS—the original and still widely-used module system in Node.js.
Why Use Modules?
Imagine building a house with all materials mixed in one pile. Chaos! Similarly, putting all your code in one file becomes unmanageable:
- Organization: Group related code together
- Reusability: Use the same code in multiple places
- Maintainability: Fix bugs in one place
- Collaboration: Team members can work on different modules
- Testing: Test modules in isolation
The CommonJS Module System
CommonJS (CJS) was created specifically for Node.js in 2009. It uses:
require()to import modulesmodule.exportsorexportsto export code
Every file in Node.js is automatically a module with its own scope.
Creating a Module
Step 1: Write the Module
// greetings.js
function sayHello(name) {
return `Hello, ${name}!`;
}
function sayGoodbye(name) {
return `Goodbye, ${name}!`;
}
const defaultGreeting = "Welcome!";
// Export functions and variables
module.exports = {
sayHello,
sayGoodbye,
defaultGreeting
};
Step 2: Use the Module
// app.js
const greetings = require('./greetings');
console.log(greetings.sayHello('Alice'));
// Output: Hello, Alice!
console.log(greetings.sayGoodbye('Bob'));
// Output: Goodbye, Bob!
console.log(greetings.defaultGreeting);
// Output: Welcome!
Different Export Patterns
Pattern 1: Exporting an Object
// utils.js
module.exports = {
formatDate: (date) => date.toISOString(),
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1),
randomNumber: (max) => Math.floor(Math.random() * max)
};
// Using it
const utils = require('./utils');
console.log(utils.capitalize('hello'));
Pattern 2: Exporting a Single Function
// logger.js
module.exports = function(message) {
console.log(`[${new Date().toISOString()}] ${message}`);
};
// Using it
const log = require('./logger');
log('Server started');
Pattern 3: Exporting a Class
// User.js
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greet() {
return `Hi, I'm ${this.name}`;
}
}
module.exports = User;
// Using it
const User = require('./User');
const user = new User('Alice', 'alice@example.com');
console.log(user.greet());
module.exports vs exports
This is a common source of confusion. Here's the key:
// At the start of every module, Node.js does:
// exports = module.exports = {}
// You can add properties to exports
exports.hello = 'world';
exports.foo = 'bar';
// This works because exports and module.exports point to the same object
// But you CAN'T reassign exports
exports = { new: 'object' }; // This breaks the reference!
// module.exports still points to {}
// Instead, reassign module.exports
module.exports = { new: 'object' }; // This works!
The Rule
- Use
exports.property = valueto add properties - Use
module.exports = valueto export something new entirely
// Safe patterns:
// 1. Adding to exports
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
// 2. Replacing module.exports
module.exports = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
// 3. Exporting a single thing
module.exports = function() { };
module.exports = class User { };
The require() Function
How require() Works
- Resolve the file path
- Load the file contents
- Wrap the code in a function
- Execute the code
- Cache the result
- Return
module.exports
Module Wrapper
Node.js wraps every module in a function:
(function(exports, require, module, __filename, __dirname) {
// Your code here
});
This is why you have access to these variables in every file!
Requiring Different Types
Local Modules (Your Files)
// Use relative paths starting with ./ or ../
const utils = require('./utils'); // Same directory
const helper = require('./lib/helper'); // Subdirectory
const config = require('../config'); // Parent directory
Built-in Modules
// No path needed for built-in modules
const fs = require('fs');
const path = require('path');
const http = require('http');
const crypto = require('crypto');
npm Packages
// After npm install, use package name
const express = require('express');
const lodash = require('lodash');
const axios = require('axios');
Module Caching
Modules are cached after first load:
// counter.js
let count = 0;
module.exports = {
increment: () => ++count,
getCount: () => count
};
// app.js
const counter1 = require('./counter');
const counter2 = require('./counter'); // Same instance!
counter1.increment();
counter1.increment();
console.log(counter1.getCount()); // 2
console.log(counter2.getCount()); // 2 (same module!)
console.log(counter1 === counter2); // true
Practical Example: Building a Calculator Module
Common Mistakes
1. Forgetting the ./ for Local Files
// Wrong - Node.js looks for a package named 'utils'
const utils = require('utils');
// Correct - relative path to your file
const utils = require('./utils');
2. Reassigning exports
// Wrong - breaks the reference
exports = { hello: 'world' };
// Correct
module.exports = { hello: 'world' };
// or
exports.hello = 'world';
3. Circular Dependencies
// a.js
const b = require('./b');
module.exports = { name: 'A' };
// b.js
const a = require('./a'); // Gets incomplete 'a'!
module.exports = { name: 'B' };
Avoid circular dependencies by restructuring your code.
Key Takeaways
- CommonJS uses
require()to import andmodule.exportsto export - Every file is a module with its own scope
- Use
./for local files, no prefix for built-in/npm modules module.exportsandexportsstart pointing to the same object- Modules are cached—
require()returns the same instance - Always use relative paths for your own modules
Summary
CommonJS is the traditional module system in Node.js. It's synchronous, straightforward, and still widely used. You've learned how to create modules, export functions and values, and import them in other files. Understanding CommonJS is essential because you'll encounter it in countless Node.js projects.
Next, we'll explore ES Modules—the newer, standardized module system that's now supported in Node.js.

