Modules in Node.js

June 24, 2019

I’ve found this to be a point of confusion for developers coming from other languages, so I thought I’d jot down some notes.


Every file (module!) in Node.js is wrapped in the following function [1]:

(function (exports, require, module, __filename, __dirname) {
// Module code actually lives in here

Therefore, variables are not by default put in the global scope. Things are only exposed when you edit module.exports.

exports vs. module.exports

In JS, objects and arrays are assigned by reference. module.exports is simply an object that the require function returns when called with module’s name as its input.

exports, on the other hand, is a reference to module.exports, nothing more. Here’s how to not abuse it:

// Don't do this!
// in ./a.js
exports = { name: 'Alex' }
// in index.js
const a = require('./a') // {}

Since exports is a reference, you can’t overwrite it. module.exports will not get updated.

// in ./a.js = 'Alex'
// in index.js
const a = require('./a') // { name: 'Alex' }

This is the proper use of exports. Personally, I’d rather only use module.exports, so I don’t have to think about this, and if I assign it to a new object it’s fine.

// Both of these are fine
// in a.js
module.exports = { name: 'Alex' }
// in a.js = 'Alex'


The first time modules are read, they are cached in require.cache. This is a classic example of memoization.

const filename = 'a.js'
const a = require(`./${filename}`)
a === require
.exports // true


  1. Variables in a file (a module) are constrained to that scope. This is why you see so much top-level variable declaration in Node.
  2. module.exports is an object that the require function returns when it receives the module’s name as an input. exports is a reference to that object.
  3. Modules are cached the first time they’re read.


  1. Node.js docs
  2. Node.js Design Patterns

Join the mailing list

Get the latest posts in your inbox. No spam, ever.