Understanding module.exports in Node.js: A Complete Guide π
When I was reading through a some.config.js file in a project, I came across this line:
module.exports = { ... };
At first glance, it looked ordinary. But then I asked myself:
π How does module.exports really work in Node.js?
This question sent me on a deep dive into Nodeβs module system β from how files are wrapped, to how require() works, and how exports differs from module.exports.
Hereβs what I learned (and what every JavaScript/Node developer should know) π
  
  
  πΉ What is module.exports in Node.js? π‘
In Node.js, every file is treated as a module.
When Node loads a file, it doesnβt run it directly. Instead, it wraps your code like this:
(function (exports, require, module, __filename, __dirname) {
  // your file content
});
This wrapper gives each file access to:
exportsrequiremodule__filename__dirname
Thatβs why you can use module.exports anywhere in your file without explicitly importing it.
  
  
  πΉ The Relationship Between exports and module.exports π
At the beginning of every module, Node does this:
exports = module.exports = {};
This means:
- 
exportsis just a reference tomodule.exports. - By default, both point to the same object.
 - Whatever you add to either (without reassigning) will be exported.
 
β
 Example:
exports.a = 10;          // works
module.exports.b = 20;   // works
Result:
{ a: 10, b: 20 }
πΉ The Common Mistake β οΈ
If you reassign exports, you break the link:
// β Won't work
exports = function () {
  console.log("Hello");
};
Now exports points to a new function, but module.exports is still {}.
So nothing gets exported.
β
 Correct way:
module.exports = function () {
  console.log("Hello");
};
  
  
  πΉ How Does require() Work? π
When you write:
const myModule = require("./myModule");
Hereβs the behind-the-scenes flow:
- Node resolves the file path (
./myModule.js) - Checks the module cache (modules load once per runtime)
 - Wraps and executes the file inside the function wrapper
 - Returns 
module.exports 
π‘ Thatβs why require() always gives you module.exports β not exports.
  
  
  πΉ Example: Using module.exports in Practice π
// user.js
module.exports = {
  name: "Yogesh",
  greet() {
    console.log(`Hello, ${this.name}`);
  }
};
// app.js
const user = require("./user");
console.log(user.name); // Yogesh
user.greet();           // Hello, Yogesh
πΉ CommonJS vs ES Modules (CJS vs ESM) β‘
Modern Node.js supports both CommonJS (CJS) and ES Modules (ESM).
β
 CommonJS (default in Node)
// math.js
module.exports = {
  add: (a, b) => a + b
};
// app.js
const math = require("./math");
console.log(math.add(2, 3));
β
 ES Modules (modern JS standard)
// math.mjs
export function add(a, b) {
  return a + b;
}
// app.mjs
import { add } from "./math.mjs";
console.log(add(2, 3));
π Key differences:
- 
Syntax β 
module.exportsvsexport/import - Loading β CommonJS is synchronous; ES Modules are asynchronous
 - Exports β CommonJS typically exports a single object; ESM supports multiple named exports
 - Future-proofing β ESM is the official JavaScript standard, better for modern apps
 
πΉ Best Practices β
- Use 
exports.x = β¦for adding multiple exports - Use 
module.exports = β¦when exporting one main thing (function, class, or object) - Prefer ES Modules (
import/export) in new projects if supported 
β¨ Conclusion
That little module.exports line in some.config.js turned out to be a doorway into Node.js internals.
What looks simple β
module.exports = { ... };
β is backed by a powerful module system that:
- Wraps files in functions
 - Links 
exportsandmodule.exports - Returns only 
module.exportsthroughrequire() - Supports both CommonJS and ES Modules
 
Next time you use module.exports, remember:
Itβs not just boilerplate β itβs the heart of how Node.js manages modules.
βοΈ Written by Yogesh Bamanier
π LinkedIn
    
Top comments (0)