module.exports & exports
Every CommonJS module has a module object, and module.exports is what require() hands back to whoever imports it. There is also a second name, exports, that confuses nearly everyone at first. This page makes the relationship precise so you never accidentally export nothing again.
The mental model
At the very start of every module, Node effectively runs this line for you:
// Conceptually, Node does this before your code runs:
var exports = module.exports = {}So exports and module.exports both point at the same empty object. require only ever returns module.exports — exports is just a convenient shorthand reference to it. Everything that follows is a consequence of that one fact.
Why exports = {...} silently fails
// WORKS — mutates the shared object
exports.add = (a, b) => a + b
// BROKEN — reassigns the local, module.exports is untouched
exports = { add: (a, b) => a + b } // require() returns {} !
// WORKS — assign to module.exports directly
module.exports = { add: (a, b) => a + b }The two export styles
Goal | Write | Import as |
|---|---|---|
A bag of named things |
|
|
One single thing |
|
|
Add one named export |
|
|
Named exports — a utility collection
// strings.js
exports.upper = (s) => s.toUpperCase()
exports.lower = (s) => s.toLowerCase()
// → require('./strings') gives { upper, lower }Single export — the module IS one value
// User.js
class User { /* ... */ }
module.exports = User
// → const User = require('./User')Common rule of thumb
Exports are live references, not copies
Because require returns the actual module.exports object, mutations made after export are visible to importers (the object is shared). But rebinding a variable inside the module does not propagate, because the importer captured the value, not the binding:
counter.js
let count = 0
exports.count = count
exports.increment = () => { count++; exports.count = count }const c = require('./counter')
console.log(c.count) // 0
c.increment()
console.log(c.count) // 1 — because increment reassigned exports.countExporting a function with attached properties
A handy pattern — export a primary function that also carries helpers, exactly how many libraries (like Express) ship:
function createApp(opts) { /* ... */ }
createApp.version = '1.0.0'
createApp.defaults = { port: 3000 }
module.exports = createApp
// → const createApp = require('./app'); createApp.version