Modules and npm
CommonJS modules
Use require and module.exports in projects that use Node’s original module system.
8 minute lesson
CommonJS loads a module with require() and exposes values through module.exports.
// math.cjs
function add(a, b) {
return a + b
}
module.exports = { add }
// app.cjs
const { add } = require("./math.cjs")
console.log(add(2, 3))
Node wraps each CommonJS file in a function. That gives the module its own top-level scope and the local values require, module, exports, __filename, and __dirname. Variables declared in math.cjs do not become globals.
Relative requests such as ./math.cjs are resolved from the requiring file, not from the terminal’s current directory. Package requests such as require('fastify') are resolved through node_modules. The node: prefix makes a built-in explicit:
const { readFile } = require('node:fs')
The value returned by require() is module.exports. The exports variable starts as a shortcut to that same object, so this works:
exports.add = add
Reassigning the shortcut does not work:
exports = { add } // require() still receives the old module.exports
Assign a replacement value to module.exports instead.
CommonJS modules are cached by resolved filename after the first load. Requiring the same file again normally returns the same exported object without rerunning its top-level code. This is useful for shared state but makes import-time side effects harder to test and reason about. Export a function when work should happen for every call.
Use .cjs for an explicit CommonJS file. A .js file is also CommonJS when the nearest package.json declares "type": "commonjs"; being explicit avoids tools guessing.
Your action is to add a top-level console.log() to math.cjs, require it twice, and explain why the message appears once. Then reproduce and fix the exports = { add } mistake.
Lesson completed