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:
- Resolution: Node.js resolves the relative path to
an absolute file path on the file system (e.g.,
/app/src/myModule.js). - Cache Check: Node.js checks
require.cacheto see if a key corresponding to this absolute path exists. - Module Loading and Execution: Since the cache is
empty for this path, Node.js instantiates a new
Moduleobject, wraps the module’s code in a wrapper function to provide local variables likeexports,require,module,__filename, and__dirname, and executes it. - Caching: The populated
Moduleinstance is stored insiderequire.cache[resolvedPath]. - Return Value: The value of
module.exportsis 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:
- Path Resolution: The path is resolved to the
absolute path (
/app/src/myModule.js). - Cache Lookup: Node.js checks
require.cache[resolvedPath]. - Immediate Return: Finding an existing entry,
Node.js immediately returns
require.cache[resolvedPath].exportswithout 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:
- Inspecting the Cache: You can view the cached
module object using
require.cache[require.resolve('./myModule')]. - Invalidating the Cache: Deleting an entry via
delete require.cache[require.resolve('./myModule')]forces Node.js to re-read and re-evaluate the file on the nextrequire()call. This technique is commonly used in testing environments and development hot-reloading tools.