Sitemap

Understanding CommonJS (CJS) Module System: module.exports vs exports

4 min readDec 17, 2024

--

Press enter or click to view image in full size

In the world of Node.js, the CommonJS (CJS) module system is the default method for organizing and exporting functionality. Two key elements that often create confusion for developers — especially beginners — are module.exports and exports. Both are used to export functionality from a module, but understanding their differences is crucial for working effectively in Node.js.

In this post, we’ll break down the differences between module.exports and exports, explore when and how to use them, and highlight some best practices to avoid common pitfalls.

What Are module.exports and exports?

Before diving into the differences, it’s important to understand that both module.exports and exports are used to export values from a Node.js module.

  • module.exports is the official object that gets returned when a module is require()d in another file.
  • exports is a shorthand reference to module.exports, making it easier to add multiple properties to the exported object.

Let’s break this down further with examples to clarify how they work.

1. module.exports - The Official Export Object

The module.exports is the actual object that gets returned when a module is imported using require(). This is the official export object, and assigning any value directly to it will replace whatever was previously assigned. Whether you're exporting a single function, an object, or an array, you assign it directly to module.exports.

Example: Exporting a Single Function

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

// main.js
const add = require('./math');
console.log(add(2, 3)); // Output: 5

Here, we’re directly assigning the add function to module.exports. When require('./math') is called in main.js, the add function is returned.

Key Takeaway:

If you want to export a single entity — like a function, class, or object — you should assign it directly to module.exports. This way, the entire exported module is replaced by that entity.

2. exports - The Shortcut to module.exports

In CommonJS, exports is a reference to module.exports, making it easier to add multiple properties to the module without directly manipulating the module.exports object.

You can add properties to exports, which will automatically be added to module.exports since they both refer to the same object.

Example: Exporting Multiple Properties

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

exports.subtract = function(a, b) {
return a - b;
};

// main.js
const math = require('./math');
console.log(math.add(2, 3)); // Output: 5
console.log(math.subtract(5, 3)); // Output: 2

In this example, exports.add and exports.subtract are added to the module. When require('./math') is called in main.js, both functions are accessible.

Key Takeaway:

Use exports when you want to add multiple properties or functions to your module, rather than replacing the entire module export.

Key Differences Between module.exports and exports

While both module.exports and exports are used to export values, there are critical differences in how they work:

  1. Assignment to exports:
  • If you use exports to add properties to the module, it works fine because exports is just a reference to module.exports.
  • However, if you assign a new value to exports (e.g., exports = {}), it breaks the connection to module.exports. This can lead to unexpected behavior and errors.
// math.js
exports = { add: function(a, b) { return a + b; } };
// main.js will not work correctly because exports is now
// detached from module.exports
const math = require('./math');
console.log(math.add(2, 3)); // Undefined or error

2. Overriding module.exports:

  • When you directly assign to module.exports, you completely replace the export object, which is useful when you want to export a single entity, such as a function or class.
// math.js
module.exports = function add(a, b) { return a + b; };

This approach completely replaces the previous content of module.exports, which can be helpful in cases where the module represents a single function or object.

3. Combining module.exports and exports:

  • While you can use both module.exports and exports together, doing so can create confusion. If you assign a value directly to module.exports, it will override exports, making it a detached reference.
// math.js
module.exports = function(a, b) { return a + b; };
exports.add = function(a, b) { return a - b; }; // This doesn't work

In this case, the assignment to module.exports overrides the exports reference, and the add function will not be exported.

Best Practices for Using module.exports and exports

To avoid common mistakes, here are some best practices for using module.exports and exports:

  • Use module.exports when you want to export a single entity like a function, object, or class. This ensures clarity and avoids overwriting issues.
// math.js
module.exports = function add(a, b) { return a + b; };
  • Use exports when you want to export multiple functions or properties. This allows you to easily add various exports without replacing the entire export object.
// math.js
exports.add = function(a, b) { return a + b; };
exports.subtract = function(a, b) { return a - b; };
  • Avoid combining module.exports and exports in the same module unless you have a clear understanding of how both work. If you assign a new value to exports, it will break the reference to module.exports.
  • Be cautious with reassigning exports. If you assign a new object to exports, it detaches it from module.exports and can result in unexpected behavior.

Conclusion

Understanding the differences between module.exports and exports is crucial for building maintainable and error-free modules in Node.js. By adhering to best practices and understanding when to use each method, you can avoid common pitfalls and improve the readability and structure of your code. Whether you're working with simple exports or complex module systems, mastering these tools will help you take full advantage of the CommonJS module system.

Remember: module.exports is for single exports, and exports is for adding multiple properties—just make sure not to overwrite exports if you want to maintain proper module exports!

--

--

Aditya Yadav
Aditya Yadav

Written by Aditya Yadav

Software Engineer who talks about tech concepts in web development https://www.linkedin.com/in/aditya-yadav-01/