Node.js modules (CommonJS modules)

Javascript has been around for a while and, up until 2015, there were no official ways to deal with code separated in multiple files (modules). Indeed, ES2015 (ECMAScript 2015) (initially known as ES6) contains the first specification from the “ECMAScript Harmony” project which introduce modules, classes, etc. Sadly, Node.js is not quite ready for the implementation of ECMAScript 2015 modules. In version current version (13.11.0), it’s offered as “Stability: 1 - Experimental”. Because of this state of affairs, multiple libraries and frameworks (CommonJS, ADM, UMD, RequireJS, etc) have tried over the years to remediate this shortcoming.

NodeJS has been using and is currently using CommonJS (at least until it is fully supporting the ES2015 Modules).

Here are some examples:

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//main.js
const add = require('./add')
const { minus } = require('./minus')
const { mult, div } = require('./other-operators')
const others = require('./other-operators')
const mult2 = require('./other-operators').mult

console.log(add(4,2))
console.log(minus(4,2))
console.log(mult(4,2))
console.log(div(4,2))
console.log(others.mult(4,2))
console.log(others.div(4,2))
console.log(mult2(4,2))

Please note the different forms that the variable assignation and require function can take.

 

The action performed by the require

When you define the files that will be imported via require, you need to keep the following template in mind:

1
2
3
4
5
6
7
8
9
//Template to keep in mind!
var module = { exports: {} };
var exports = module.exports;

// ------------------------------
// WHAT-EVER-IS-IN-YOUR-FILE
// ------------------------------

return module.exports;

Require act as if it was creating 2 objects; exports and module.exports. The variable exports is just a shortcut to module.exports and they are basically pointing to the same object. We will see later on that we may find ourselves in a bit of trouble of we miss use this shortcut.

Now that the stage is set, let’s actually look at our imported files one by one.

 

The add.js file

1
2
3
4
//add.js
module.exports = function ( first, second ) {
return first + second
}

This is the easiest form. The file exports an anonymous function.

In this case, our require in main.js:

1
2
3
//main.js
const add = require('./add')
//...

Can be interpreted like this:

1
2
3
4
5
//main.js
const add = function ( first, second ) {
return first + second
}
//...

 

The minus.js file

1
2
3
4
//minus.js
exports.minus = function ( first, second ) {
return first + second
}

Please remember that we are importing it with the following code:

1
2
3
4
//main.js
//...
const { minus } = require('./minus')
//...

Please notice the brackets surrounding our minus function. In order to understand this one, we will have to explore a new syntax provided by ES6. It’s called the destructuring assignment.

I won’t cover this syntax at length here but here’s a quick example:

1
2
3
4
const { p, q } = { a: "test", p: 42, q: true, z: "whatever" };

console.log(p); // 42
console.log(q); // tru

As you can see, it’s a quick way to extract variables and values from an object.

It’s effectively like doing this:

1
2
3
4
5
6
const o = { a: "test", p: 42, q: true, z: "whatever" };
const p = o.p;
const q = o.q;

console.log(p); // 42
console.log(q); // tru

So, basically it’s extracting variables (destructuring) the object based on its properties.

Then, when we used the following code:

1
2
3
4
// main.js
//...
const { minus } = require('./minus')
//...

And the definition in minus.js :

1
2
3
4
//minus.js
exports.minus = function ( first, second ) {
return first + second
}

Here’s how we can interpret that call:

1
2
3
4
5
6
7
8
const { minus } = { minus : function ( first, second ) {
return first + second
}}

// Which is like :
const minus = function ( first, second ) {
return first + second
}

We’ve created a variable minus pointing to the value of the minus property (which is a function) returned by the require statement.

In summary, this is why we had to use brackets {} to get our function. The add.js was returning a single function so, we could just pick it up directly into the variable add. But, in the case of the minus.js, the require was returning an object and we had to destructure the object into our minus variable.

 

The other_operators.js file

1
2
3
4
5
6
7
8
9
10
11
12
13
//other_operators.js
function multiplication (first, second) {
return first * second
}

function div (first, second) {
return first / second
}

module.exports = {
mult: multiplication,
div: div
}

We can apply pretty much the same logic than we used for destructuring the object returned by require in the minus.js file, except that we have 2 functions:

1
2
3
4
//main.js
//...
const { mult, div } = require('./other-operators')
//...

For the next line in main.js, we wanted to show that we do need to destructure the object returned by require:

1
2
3
4
//main.js
//...
const others = require('./other-operators')
//...

We just have to remember that the variable others is an object, not a function. So we have to call the mult and div function this way:

1
2
3
4
5
//main.js
//...
console.log(others.mult(4,2))
console.log(others.div(4,2))
//...

The next one is a bit ugly but I have seen this in the past…

1
2
3
4
//main.js
//...
const mult2 = require('./other-operators').mult
//...

It’s basically like getting the value of mult (function) directly from the object returned by require

1
2
const johnsname = { name: "john" }.name
console.log(johnsname) //resulting in "john"

 

Things to avoid

As we pointed out earlier, the exports variable is just a shortcut for module.exports. They are both pointing at the same object. It’s easy to get wrapped up in your code and do something like this:

1
exports = { test : function () { console.log ("ops!"); } }

The problem with this, is that you just moved the pointer of exports to a new object so module.exports and exports are no longer pointing at the same object… but the require statement will only return the object that module.exports is pointing to!

 

Lost in space?

If you become unsure of what is on your module.exports, you can always temporarily add the following code at the end of the file and run it through node.

1
console.log(process.mainModule.exports)

For example, if we add this line at the end of other-operators.js, we would get the following output:

1
{ mult: [Function: multiplication], div: [Function: div] }

Summary

In summary, just keep in mind the following pattern:

1
2
3
4
5
6
7
8
9
//Template to keep in mind!
var module = { exports: {} };
var exports = module.exports;

// ------------------------------
// WHAT-EVER-IS-IN-YOUR-FILE
// ------------------------------

return module.exports;

Also, make sure you understand the destructuring assignment.

If you are in doubt, temporary add this code to your file and run it through node:

1
console.log(process.mainModule.exports)

Finally, if you are returning an object and using the exports variable shortcut instead of the module.exports variable, make sure that you are adding properties to the object, not overriding it. And if you are just returning a function, use the module.exports. And, at last, if you just don’t want to bother, just use module.exports!