module resolution
1. What is "module resolution"
Module resolution is the process by which Node.js finds the physical file (module) by the name specified in
require()orimport.
Put simply:
const express = require('express');means:
Node.js has to find exactly where the express module is located: in the core, in
node_modules, or on a local path.
2. The general search sequence
When Node.js encounters require('something'), it goes through five stages:
- Checks whether it is a built-in (core) module;
- Checks whether it is a path to a file (
./,../,/); - Otherwise treats it as a module from
node_modules; - Looks for it in the nearest
node_modules, walking up the directory tree; - If not 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 installation required |
| 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 | Parsed automatically or loaded as binary |
4. The module resolution algorithm (for CommonJS)
Example:
const utils = require('./utils');Node.js does the following:
- Checks whether
'./utils'is a path:
- whether it starts with
./,../, or/: yes.
- Converts the path to an absolute one:
/Users/tim/project/utils- Tries the following variants (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 packages from node_modules
Example:
const express = require('express');Node.js:
- Looks for a built-in module
express-> no. - Looks for
node_modules/expressin the current folder. - If not found -> moves 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 file
express/package.json. - Looks for the
"main"field in it:
{
"name": "express",
"main": "index.js"
}- Loads
/node_modules/express/index.js.
6. A visual example
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 regular JavaScript file;.json- automatically parsed into an object;.node- a binary native module (C++).
Example:
require('./config'); // will try config.js → config.json → config.node8. Module resolution for ES Modules (ESM)
For ESM (import/export) the algorithm is different - stricter:
- You must specify the exact extension (
.js,.mjs,.json); - Folders with
index.jsare not searched for automatically; __dirnameand__filenameare absent;"exports"inpackage.jsondetermines the available paths.
Example:
import utils from './utils/index.js'; // the extension is mandatory9. How package.json affects module resolution
Node.js looks at the fields:
| Field | Purpose |
|---|---|
"main" | The main entry point for CommonJS |
"exports" | A modern alternative for ESM / CJS |
"type" | Determines 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 will use "exports" instead of "main"
if it is specified - this is the modern way to control which files can be imported from outside.
10. Caching during module resolution
Once Node.js has resolved a module and loaded it, the result is stored in require.cache:
console.log(require.cache);A repeated require() does not execute the file again, it simply returns its exports from the cache.
This speeds up loading and prevents recursive cycles (circular dependencies).
11. Tools for debugging resolution
If you want to see exactly where a module is 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 by which Node.js determines where a module specified in
require()orimportis physically located, going through built-in modules, local files, andnode_modulesuntil it finds the right file.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.