Bird
Raised Fist0
Node.jsframework~15 mins

CommonJS require and module.exports in Node.js - Deep Dive

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Overview - CommonJS require and module.exports
What is it?
CommonJS is a system used in Node.js to organize and share code between files. It uses two main parts: 'require' to load code from other files, and 'module.exports' to make code available to others. This helps break big programs into smaller, manageable pieces. It works by loading and running files when needed.
Why it matters
Without CommonJS, all code would be in one big file, making it hard to read, fix, or reuse. CommonJS solves this by letting developers split code into modules that can be shared easily. This makes building and maintaining programs faster and less error-prone. It also allows using third-party code libraries smoothly.
Where it fits
Before learning CommonJS, you should understand basic JavaScript syntax and how files work on your computer. After mastering CommonJS, you can learn about newer module systems like ES Modules or how to bundle code for browsers. It fits early in the Node.js learning path as the foundation for modular code.
Mental Model
Core Idea
CommonJS lets you split your program into separate files that share code by exporting and requiring modules, like passing tools between workers in a workshop.
Think of it like...
Imagine a workshop where each worker has a toolbox. If one worker needs a hammer, they ask another worker to lend it. 'module.exports' is like putting the hammer in your toolbox to share, and 'require' is like borrowing that hammer from someone else's toolbox.
┌─────────────┐       ┌─────────────┐
│ moduleA.js  │       │ moduleB.js  │
│             │       │             │
│ module.exports ─────▶│ const tool = require('./moduleA')
│ (exports tools)│     │ (borrows tools)│
└─────────────┘       └─────────────┘
Build-Up - 7 Steps
1
FoundationWhat is a Module in Node.js
🤔
Concept: A module is a single file that contains code which can be reused elsewhere.
In Node.js, every JavaScript file is a module. You can write functions, variables, or objects inside it. By default, these are private to the file unless you explicitly share them.
Result
You understand that code is organized into files called modules, each with its own scope.
Knowing that each file is a separate module helps you think about code as small, independent parts rather than one big block.
2
FoundationUsing module.exports to Share Code
🤔
Concept: module.exports is the object that a module returns when another file requires it.
To share something from a module, assign it to module.exports. For example, module.exports = function() { return 'Hello'; } makes that function available to others.
Result
You can make functions or data available outside the module by assigning them to module.exports.
Understanding module.exports is key to controlling what your module shares and what stays private.
3
IntermediateLoading Modules with require
🤔Before reading on: do you think require loads code once or every time it is called? Commit to your answer.
Concept: require is a function that loads and runs another module, returning what that module exports.
When you call require('moduleName'), Node.js finds the file, runs it if not run before, and returns module.exports from that file. This lets you use code from other files easily.
Result
You can use require to bring in code from other modules and use their exported parts.
Knowing that require runs the module once and caches it explains why changes in the module after loading don't affect the current program.
4
IntermediateExports vs module.exports Difference
🤔Before reading on: do you think assigning to exports changes module.exports automatically? Commit to your answer.
Concept: exports is a shortcut to module.exports but assigning exports directly breaks the link.
exports is initially a reference to module.exports. You can add properties to exports like exports.foo = 1, but if you assign exports = something else, it no longer points to module.exports, so the exported value won't change.
Result
You learn to use either module.exports = value or add properties to exports, but not assign exports directly.
Understanding this prevents bugs where your module exports nothing because you overwrote exports instead of module.exports.
5
IntermediateHow require Caches Modules
🤔Before reading on: do you think require loads the same module file multiple times or caches it? Commit to your answer.
Concept: Node.js caches modules after the first load to improve performance and keep state.
When require loads a module, it stores the result in a cache. Later require calls for the same module return the cached exports without re-running the code. This means modules behave like singletons.
Result
You understand that require returns the same object every time for a module, preserving state across requires.
Knowing about caching helps you design modules that maintain state or avoid side effects when loaded multiple times.
6
AdvancedCircular Dependencies in CommonJS
🤔Before reading on: do you think circular requires cause errors or partial exports? Commit to your answer.
Concept: When two modules require each other, Node.js handles it by giving partial exports during loading to avoid infinite loops.
If moduleA requires moduleB and moduleB requires moduleA, Node.js returns an incomplete exports object for the module still loading. This can cause unexpected undefined values if not handled carefully.
Result
You learn to recognize and avoid pitfalls with circular dependencies or design modules to handle partial exports safely.
Understanding circular dependencies prevents bugs that are hard to trace and helps design cleaner module relationships.
7
ExpertModule Wrapping and Execution Context
🤔Before reading on: do you think module code runs in the global scope or a special wrapper? Commit to your answer.
Concept: Node.js wraps each module in a function to provide private scope and module-specific variables.
Behind the scenes, Node.js wraps your module code in a function like (function(exports, require, module, __filename, __dirname) { /* your code */ }). This means variables you declare are local to the module, not global.
Result
You understand why variables don't leak globally and how module variables like exports and require are available automatically.
Knowing about the wrapper function explains module isolation and why you can use require and exports without importing them.
Under the Hood
When you call require('file'), Node.js resolves the file path, reads the file content, and wraps it in a function with parameters exports, require, module, __filename, and __dirname. It then executes this function, passing in objects for exports and module. The module.exports object is what require returns. Node.js caches this result so subsequent requires return the cached exports without re-executing the file.
Why designed this way?
CommonJS was designed to provide a simple, synchronous module system for server-side JavaScript before browsers supported modules. The wrapper function isolates module scope to avoid polluting the global environment. Caching improves performance and allows modules to maintain state. Alternatives like asynchronous or static module systems were not practical for early Node.js use cases.
┌───────────────────────────────┐
│ require('module') called      │
├───────────────────────────────┤
│ 1. Resolve file path           │
│ 2. Check cache                │
│   ├─ If cached: return exports│
│   └─ Else:                    │
│      a) Read file             │
│      b) Wrap in function      │
│         (exports, require,    │
│          module, __filename,  │
│          __dirname)           │
│      c) Execute function      │
│      d) Cache module.exports  │
│      e) Return module.exports │
└───────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does assigning directly to exports change what the module exports? Commit to yes or no.
Common Belief:Assigning a new value to exports changes the exported module.
Tap to reveal reality
Reality:Assigning directly to exports breaks the link to module.exports, so the module exports remain unchanged unless module.exports is assigned.
Why it matters:This causes modules to export empty objects unexpectedly, leading to bugs that are hard to diagnose.
Quick: Does require reload the module code every time it is called? Commit to yes or no.
Common Belief:require runs the module code every time it is called, so changes in the module file affect subsequent requires immediately.
Tap to reveal reality
Reality:require caches the module after the first load and returns the cached exports on subsequent calls without re-running the code.
Why it matters:Expecting fresh code on every require can cause confusion and bugs when modules maintain state or when hot-reloading is attempted.
Quick: Do circular dependencies cause runtime errors always? Commit to yes or no.
Common Belief:Circular dependencies always cause errors or crashes.
Tap to reveal reality
Reality:Node.js handles circular dependencies by providing partial exports during loading, which can cause undefined values but not always errors.
Why it matters:Misunderstanding this leads to ignoring circular dependencies or not designing modules to handle partial exports safely.
Quick: Is CommonJS module code executed in the global scope? Commit to yes or no.
Common Belief:Module code runs in the global scope, so variables declared are global.
Tap to reveal reality
Reality:Node.js wraps module code in a function, so variables are local to the module and do not pollute the global scope.
Why it matters:Assuming global scope can cause naming conflicts and misunderstanding of variable visibility.
Expert Zone
1
Modules are cached by resolved absolute path, so requiring the same file via different relative paths creates separate instances.
2
module.exports can be any value (object, function, primitive), but exports is only a shortcut for adding properties to module.exports.
3
The wrapper function provides __filename and __dirname, which are not global but module-specific, enabling modules to know their location.
When NOT to use
CommonJS is synchronous and designed for server environments. For browser code or modern JavaScript, ES Modules (ESM) are preferred because they support static analysis, asynchronous loading, and better tooling. Use ESM for front-end projects or when interoperating with modern JavaScript ecosystems.
Production Patterns
In production, CommonJS modules are often bundled using tools like Webpack or Rollup for browser use. Developers use module.exports to export single functions or objects and require to import dependencies. Circular dependencies are avoided or carefully managed. Caching behavior is leveraged for singleton patterns or shared state.
Connections
ES Modules (ESM)
CommonJS is the older module system; ESM is the modern standard that builds on similar ideas but adds static structure and async loading.
Understanding CommonJS helps grasp ESM's purpose and differences, especially how modules export and import code.
Software Componentization
CommonJS modules are a form of componentization, breaking software into reusable parts.
Knowing how modules isolate code relates to general software design principles of modularity and separation of concerns.
Library Lending in Real Life
Like borrowing books from a library, require borrows code from modules, and module.exports is what the library offers to readers.
This connection shows how sharing resources efficiently is a universal concept beyond programming.
Common Pitfalls
#1Overwriting exports instead of module.exports
Wrong approach:exports = function() { return 'Hello'; };
Correct approach:module.exports = function() { return 'Hello'; };
Root cause:Misunderstanding that exports is just a reference to module.exports and assigning exports breaks the link.
#2Expecting require to reload updated module code
Wrong approach:require('./module'); // expecting fresh code every time
Correct approach:Restart the Node.js process or clear require cache manually to reload module code.
Root cause:Not knowing that require caches modules after first load.
#3Creating circular dependencies without handling partial exports
Wrong approach:moduleA.js requires moduleB.js and moduleB.js requires moduleA.js without safeguards.
Correct approach:Refactor code to avoid circular dependencies or design modules to handle partial exports safely.
Root cause:Ignoring how Node.js handles circular dependencies and partial loading.
Key Takeaways
CommonJS uses module.exports to share code and require to load it, enabling modular programming in Node.js.
Modules are wrapped in a function to provide private scope and module-specific variables like exports and require.
require caches modules after the first load, so subsequent calls return the same exports without re-running code.
Assigning directly to exports breaks the export link; always assign to module.exports to export a value.
Circular dependencies return partial exports during loading, so they must be handled carefully to avoid bugs.

Practice

(1/5)
1. What does module.exports do in a Node.js file?
easy
A. It deletes the current module from memory.
B. It imports code from another module.
C. It runs the module as a standalone program.
D. It defines what the module shares when required by another file.

Solution

  1. Step 1: Understand module.exports role

    module.exports sets the object or value that other files receive when they use require() on this module.
  2. Step 2: Differentiate from require()

    require() is used to import, while module.exports is used to export code from a module.
  3. Final Answer:

    It defines what the module shares when required by another file. -> Option D
  4. Quick Check:

    module.exports = export code [OK]
Hint: Remember: module.exports shares, require() imports [OK]
Common Mistakes:
  • Confusing require() with module.exports
  • Thinking module.exports runs code
  • Assuming module.exports deletes modules
2. Which of the following is the correct syntax to import a local module named utils.js using CommonJS?
easy
A. const utils = require('./utils');
B. const utils = require('utils');
C. import utils from './utils';
D. const utils = import('./utils');

Solution

  1. Step 1: Identify local module import syntax

    Local files require a relative path starting with './' or '../' in require().
  2. Step 2: Check each option

    const utils = require('./utils'); uses require('./utils'), which correctly imports the local utils.js file. const utils = require('utils'); misses './', so it looks for a package. import utils from './utils'; uses ES module syntax, not CommonJS. const utils = import('./utils'); uses dynamic import, not CommonJS.
  3. Final Answer:

    const utils = require('./utils'); -> Option A
  4. Quick Check:

    Local modules need './' in require() [OK]
Hint: Use './' prefix for local files in require() [OK]
Common Mistakes:
  • Omitting './' for local modules
  • Using ES module import syntax in CommonJS
  • Using import() instead of require()
3. Given the following two files, what will be logged when node app.js runs?

// math.js
module.exports.add = (a, b) => a + b;
module.exports.sub = (a, b) => a - b;

// app.js
const math = require('./math');
console.log(math.add(5, 3));
console.log(math.sub(5, 3));
medium
A. undefined and undefined
B. 8 and 2
C. Error: add is not a function
D. 5 and 3

Solution

  1. Step 1: Understand exports in math.js

    math.js exports two functions: add and sub, which add and subtract two numbers.
  2. Step 2: Trace app.js calls

    app.js requires math.js and calls math.add(5, 3) which returns 8, and math.sub(5, 3) which returns 2.
  3. Final Answer:

    8 and 2 -> Option B
  4. Quick Check:

    5+3=8 and 5-3=2 [OK]
Hint: Check exported function names and call with correct args [OK]
Common Mistakes:
  • Expecting undefined because of wrong export syntax
  • Confusing module.exports with exports shorthand
  • Forgetting to require the module
4. What is the error in the following code snippet?

// greet.js
exports = function() { return 'Hello'; };

// app.js
const greet = require('./greet');
console.log(greet());
medium
A. Cannot find module './greet'.
B. SyntaxError due to missing module.exports.
C. greet is not a function because exports was overwritten incorrectly.
D. No error; it logs 'Hello'.

Solution

  1. Step 1: Analyze exports assignment in greet.js

    Assigning directly to exports replaces the local exports variable but does not change module.exports, so require() gets an empty object.
  2. Step 2: Understand require() result in app.js

    Since module.exports was not changed, greet is an empty object, not a function, so calling greet() causes an error.
  3. Final Answer:

    greet is not a function because exports was overwritten incorrectly. -> Option C
  4. Quick Check:

    Overwrite exports breaks module.exports [OK]
Hint: Always assign to module.exports, not exports directly [OK]
Common Mistakes:
  • Assigning function directly to exports instead of module.exports
  • Expecting exports and module.exports to be the same after reassignment
  • Ignoring that require() returns module.exports
5. You want to export a single class from a module so that requiring it returns the class directly. Which is the correct way to do this in CommonJS?

class User {
  constructor(name) {
    this.name = name;
  }
}

// What should you write here?
hard
A. module.exports = User;
B. exports.User = User;
C. module.exports.User = User;
D. export default User;

Solution

  1. Step 1: Understand exporting a single value

    To export a single class so require() returns it directly, assign it to module.exports.
  2. Step 2: Compare options

    module.exports = User; assigns User directly to module.exports, so require('./module') returns the class. module.exports.User = User; and exports.User = User; export an object with User property, so require() returns an object, not the class itself. export default User; uses ES module syntax, invalid in CommonJS.
  3. Final Answer:

    module.exports = User; -> Option A
  4. Quick Check:

    Single export = module.exports = value [OK]
Hint: Assign single export directly to module.exports [OK]
Common Mistakes:
  • Using exports.User instead of module.exports for single export
  • Mixing ES module syntax with CommonJS
  • Expecting require() to return class when exporting as property