Node.js · 05/19 6 min
Modules: require, import and the cache
module.exports vs exports, the require cache, circular imports and ESM differences.
Half of "it's undefined" bugs in a Node codebase are really module bugs: something was exported under a different name, or not exported at all.
CommonJS in one picture
// math.js
function add(a, b) { return a + b; }
module.exports = { add }; // the object require() returns
// app.js
const { add } = require("./math"); // destructure the named export
const math = require("./math"); // or keep the whole object: math.add
Bugs that show up in labs
| Bug | Why it breaks | Fix |
|---|---|---|
exports = { add } |
reassigns the local variable; module.exports is still {} |
module.exports = { add } or exports.add = add |
const add = require("./math") |
you got the object, not the function: add is not a function |
destructure: const { add } = … |
module.exports = router then require("./routes").router |
the export is the router; it has no .router |
match the shape on both sides |
| two files require each other | the second sees a half-finished module.exports |
move shared code to a third module, or require lazily inside the function |
typo in path casing (./Models/user) |
works on macOS, fails on Linux CI | match the real casing |
The require cache
A module's top-level code runs once. Every later require returns the same object, so a module is a singleton:
// db.js
const users = []; // shared by every file that requires db.js
module.exports = { users };
That's handy for an in-memory store, and a trap for tests: state leaks between tests unless something resets it (beforeEach(() => users.length = 0)).
ES modules
import { add } from "./math.js"; // extension required in Node ESM
export function add(a, b) { … }
export default router; // import router from "./routes.js"
__dirnameandrequiredon't exist in ESM. Useimport.meta.url(orimport.meta.dirnamein recent Node).- Imports are hoisted and live: you can't conditionally
importat the top level; useawait import(...). - Mixing default and named exports is the ESM version of the
exportsbug:import router fromvsimport { router } frommust match what the file exports.
Practise it now
Next in Node.js
Errors in Node and Express