module resolution
1. What "module resolution" is
Module resolution is the process Node.js uses to find the physical file (module) named in
require()orimport.
In other words:
const express = require('express');means:
Node.js has to figure out exactly where the express module lives, in core, in
node_modules, or at a local path.
2. The general search sequence
When Node.js hits require('something'), it goes through five stages:
- Checks whether it's a core (built-in) module;
- Checks whether it's a file path (
./,../,/); - Otherwise assumes it's a module from
node_modules; - Looks for it in the nearest
node_modules, walking up the directory tree; - If nothing is found, throws a
MODULE_NOT_FOUNDerror.
3. Types of modules Node.js can resolve
| Type | Example | Description |
|---|---|---|
| Core (built-in) | fs, path, os, http | Built into Node.js, no install needed |
| Local files | ./utils.js, ../config.json | Your own files |
| Packages (npm) | express, chalk, mongoose | Looked up in node_modules |
| JSON / binary | config.json, addon.node | Auto-parsed or loaded as binary |
4. The module resolution algorithm (for CommonJS)
Example:
const utils = require('./utils');Node.js does this:
- Checks whether
'./utils'is a path:
- does it start with
./,../or/? Yes.
- Turns the path into an absolute one:
/Users/tim/project/utils- Tries the following, in order:
utils.js
utils.json
utils.node
utils/index.js
utils/index.json
utils/index.node- If nothing is found → an error:
Error: Cannot find module './utils'5. The algorithm for node_modules packages
Example:
const express = require('express');Node.js:
- Looks for a built-in module named
express→ no. - Looks for
node_modules/expressin the current folder. - If not found, walks up the tree:
/Users/tim/project/node_modules/express
/Users/tim/node_modules/express
/Users/node_modules/express
/node_modules/express- Found it → reads the
express/package.jsonfile. - Looks for the
"main"field in it:
{
"name": "express",
"main": "index.js"
}- Loads
/node_modules/express/index.js.
6. Visually
project/
│
├── app.js
├── utils/
│ └── index.js
└── node_modules/
└── lodash/
├── package.json
└── lodash.jsrequire('./utils'); // → ./utils/index.js
require('lodash'); // → ./node_modules/lodash/lodash.js
require('fs'); // → a built-in Node.js module7. How Node.js chooses between .js, .json and .node
Node tries extensions in this order:
.js, a plain JavaScript file;.json, auto-parsed into an object;.node, a native binary module (C++).
Example:
require('./config'); // tries config.js → config.json → config.node8. Module resolution for ES Modules (ESM)
For ESM (import/export), the algorithm is different, and stricter:
- The exact extension must be given (
.js,.mjs,.json); - Folders with an
index.jsaren't searched for automatically; __dirnameand__filenamedon't exist;"exports"inpackage.jsondefines the available paths.
Example:
import utils from './utils/index.js'; // the extension is required9. How package.json affects module resolution
Node.js looks at these fields:
| Field | Purpose |
|---|---|
"main" | The main entry point for CommonJS |
"exports" | The modern alternative for ESM / CJS |
"type" | Sets the module type (commonjs or module) |
Example:
{
"name": "my-lib",
"main": "./dist/index.cjs",
"exports": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"type": "module"
}Node uses "exports" instead of "main"
when it's present, the modern way to control which files can be imported from outside.
10. Caching during module resolution
Once Node.js resolves a module and loads it, the result is stored in require.cache:
console.log(require.cache);A repeated require() doesn't re-run the file, it just returns its exports from the cache.
This speeds up loading and prevents recursive cycles (circular dependencies).
11. Tools for debugging resolution
To see exactly where a module is being picked up from:
node -p "require.resolve('express')"Example output:
/Users/tim/project/node_modules/express/index.jsOr for a local file:
node -p "require.resolve('./utils')"In one sentence
Module resolution is the mechanism Node.js uses to figure out where a module named in
require()orimportphysically lives, checking built-in, local, andnode_moduleslocations until it finds the right file.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.