How does `require` work? Explain module resolution and caching.

3 minintermediatenodejsrequireresolutioncachingnode_modules

Quick Answer

`require` resolves a specifier to an absolute path (core module → relative/absolute path → walking up `node_modules`), loads and executes the file once, and caches `module.exports` in `require.cache`. Subsequent `require`s of the same resolved path return the cached exports without re-running the file.

Detailed Answer

Answer:

Resolution algorithm (roughly):

  1. Core module? e.g. require('fs') → return the built-in.
  2. Relative/absolute path? ./, ../, / → resolve the file, trying extensions .js, .json, .node, then a directory's index.js or its package.json main.
  3. Bare specifier? e.g. require('express') → look in ./node_modules, then the parent directory's node_modules, walking up to the filesystem root.
require('fs');          // core
require('./utils');     // ./utils.js or ./utils/index.js
require('lodash');      // node_modules/lodash

Caching:

  • After a module runs, its module.exports is stored in require.cache, keyed by the resolved absolute path.
  • Every later require of that path returns the same object — the module body runs only once.
// counter.js
let count = 0;
module.exports = { inc: () => ++count, get: () => count };

// a.js and b.js both require('./counter') share the SAME instance

This is the basis of the singleton pattern in Node and why module top-level code (like a DB connection) runs a single time.

Cache gotchas:

  • Two different resolved paths (e.g., two copies of a package in different node_modules) are separate cached instances — a common source of "instanceof fails" or "duplicate singleton" bugs.
  • You can delete require.cache[resolvedPath] to force a reload (used by some hot-reload tools), but it's rarely a good idea in production.