How to Use Exports in NodeJS

In any substantial project, it is necessary to separate your code in different files. Node.js implements the CommonJS API standard to load modules from other files. Using exports can be a source of much confusion in Node.js. Let us explore how exports works.

Use Case 1: Exporting as an Object of functions

The most common use case is to export your functions using exports.

For example, I have a script that wants to use functions from another Node.js file.

1
2
3
4
5
6
7
8
9
10
// make_sandwich.js

var fridge = require ('./fridge');

function makeSandwich() {
  return {sandwich: fridge.bread() + ' ' + fridge.egg};
}

console.log(makeSandwich());
// => { sandwich: 'bread: 2 egg: 1' }
1
2
3
4
5
6
7
// fridge.js

exports.bread = function bread() {
  return 'bread: 2';
};

exports.egg = 'egg: 1';

Using require, Node.js will evaluate the file and load the functions defined in the exports object.

Behind the scenes, exports is just an object. Here is how it works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// fridge.js

//var exports = {};

exports.bread = function bread() {
  return 'bread: 2';
};

exports.egg = 'egg: 1';

// exports = {
//              bread: function bread() { ... },
//              egg: 'egg: 1'
//           };

You can define variables and functions in the exports object. It is exposed and can be used in other files when it is required.

What require does is to return the exports object defined in the file.

1
2
3
4
5
6
7
8
9
10
11
// make_sandwich.js
// subtituting require for the exports object

var fridge = require ('./fridge');

// var fridge = {
//                bread: function() { ... },
//                egg: 'egg: 1'
//              };

...

The variable fridge has the properties defined in the exports object.

You don’t have to use the same function name when exporting.

1
2
3
4
5
6
7
8
// fridge.js

// originally the function is called bread
exports.bread = function wholemealBread() {
  return 'bread: 2';
};

exports.egg = 'egg: 1';

It returns the same result. Only the name defined in the exports object is used.

Use Case 2: Exporting with module.exports

module.exports can be used to export the interfaces directly. For example,

1
2
3
4
5
// bread.js

module.exports = function() {
  return 'bread: 2';
};
1
2
3
4
5
6
7
8
9
10
// make_sandwich.js

var bread = require ('./bread');

function makeSandwich() {
  return {sandwich: bread() + ' with some eggs'};
}

console.log(makeSandwich());
// => { sandwich: 'bread: 2 with some eggs' }

When is this pattern used? It is used when you want to expose one variable. It could be a function, string, or any valid variable.

The code below is valid and will yield the same result if you call bread as a variable.

1
2
3
4
// bread.js
// assigning module.exports to a string

module.exports = 'bread: 2';
1
2
3
4
5
6
7
8
9
10
// make_sandwich.js

var bread = require ('./bread');

function makeSandwich() {
  return {sandwich: bread + ' with some eggs'};
}

console.log(makeSandwich());
// => { sandwich: 'bread: 2 with some eggs' }

The code below is the equivalent of the first example of fridge.js using module.exports.

1
2
3
4
5
6
7
8
9
// fridge.js

module.exports = {
  bread: function bread() {
    return 'bread: 2';
  },

  egg: 'egg: 1'
};

So what happens to the exports variable then? If module.exports is defined, the exports object will be ignored. For example, if I add another definition of bread

1
2
3
4
// bread.js

module.exports = 'bread: 2';
exports.bread = 'wheat bread: 4';
1
2
3
4
5
6
7
8
9
10
// make_sandwich.js

var bread = require ('./bread');

function makeSandwich() {
  return {sandwich: bread + ' with some eggs'};
}

console.log(makeSandwich());
// => { sandwich: 'bread: 2 with some eggs' }

It uses the definition defined in module.exports.

What if you assign a function to exports?

1
2
3
// bread.js

exports = 'bread: 2';
1
2
3
4
var bread = require ('./bread');

console.log(bread);
// => {}

Why didn’t exports have the assigned string value? exports is actually a property inside the module.

When you assign a function or string directly to exports, Javascript will treat exports as another variable. Not the exports property inside the module.

When you change the property of exports, Javascript will access the exports property inside the module and apply the changes.

A deeper explanation can be found in this Stackoverflow question.

Comments