How CommonJS Require Caching Works

In Node.js and CommonJS environments, the require() function automatically caches loaded modules to optimize performance and maintain consistent state across an application. When a file is imported for the first time, Node.js resolves its absolute file path, reads the file, executes its code, and stores the resulting module.exports object in an internal cache object called require.cache. Subsequent require() calls for the exact same file skip the evaluation step entirely and instantly return the already cached exports.

The Initial require() Sequence

When you invoke require('./myModule') for the first time, Node.js executes the following steps:

  1. Resolution: Node.js resolves the relative path to an absolute file path on the file system (e.g., /app/src/myModule.js).
  2. Cache Check: Node.js checks require.cache to see if a key corresponding to this absolute path exists.
  3. Module Loading and Execution: Since the cache is empty for this path, Node.js instantiates a new Module object, wraps the module’s code in a wrapper function to provide local variables like exports, require, module, __filename, and __dirname, and executes it.
  4. Caching: The populated Module instance is stored inside require.cache[resolvedPath].
  5. Return Value: The value of module.exports is returned to the caller.

How Subsequent Requests Are Handled

When another file or the same file executes require('./myModule') later in the application lifecycle, Node.js follows a fast-path lookup:

  1. Path Resolution: The path is resolved to the absolute path (/app/src/myModule.js).
  2. Cache Lookup: Node.js checks require.cache[resolvedPath].
  3. Immediate Return: Finding an existing entry, Node.js immediately returns require.cache[resolvedPath].exports without reading the file from the disk or executing the module code again.

Because the code is not re-executed, any top-level side effects (such as logging to the console, initializing database connections, or configuring instances) only run once.

Key Implications of CommonJS Caching

1. Modules Act as Singletons

Because the exact same module.exports reference is shared across all subsequent imports, CommonJS modules act as singletons by default. If one file mutates a property on an exported object, all other files requiring that same module will see the mutation.

2. Exact Path Resolution Defines the Cache Key

The cache is keyed by the fully resolved, canonical file path. If two different files require a module using different relative paths (such as require('../utils') vs require('./utils')), both resolve to the same absolute path and point to the identical cache entry. However, if symlinks or case-insensitive file systems cause the paths to resolve to different strings, Node.js may treat them as separate modules.

3. Handling Circular Dependencies

When module A requires module B, and module B requires module A, the caching mechanism prevents infinite loops. When module B requests module A, Node.js returns the current (possibly incomplete) module.exports object stored in require.cache before module A has finished its execution.

Manually Manipulating require.cache

The cache is exposed directly to developers as a plain JavaScript object via require.cache. It is possible to inspect or invalidate the cache programmatically: