When you have code in one file that you can or need to use in another file, you will need to export that code so that other pieces of code can access and call that code. Every language has their own way of doing this as this promotes code reuse and writing common libraries.

In Javascript in general and nodejs in particular, we have something we call modules, which need to be exported so that the code in that file is made available for other code to call. Thus the term, exporting modules.

In this post we are going to describe different ways to export modules and describe the subtle differences so that the next time you see such exporting, you know what exactly is going on. These methodologies will assume that you have a file in which you have one or more functions that you want to make available to the outside world as modules. Let’s dive into it.

Nodejs how to export and require modules

Method 1:

const exportVariable = { name: "Tesla", Field: "Automobiles"};

const exportMethod = function(input){
    return { name: input };
}

module.exports.variableExport = exportVariable;
module.exports.methodExport = exportMethod;

Simply, we initialise a function or variable and then export it giving it a name. As you can see, the name that we export to be visible to the outside world can be different than the name of the function itself. And this is how you use the function and variable in this method:

const exportFunctionAssign = require('./exportFunction');

// Methods initialized first, assigned to module.exports later.
let exportFunctionResponse = exportFunctionAssign.methodExport("SpaceX");
let exportVariableResponse = exportFunctionAssign.variableExport;

Method 2:

module.exports = {
    SuperSonicBoom: function(mach) {
        return { Mach: mach };
    }
}

This method is more direct than the previous in that we directly assign the method when declaring the module using module.exports. We call this method just like Method 1.

Method 3:

Now the above function can be exported in a more direct way, if it is the only function in this file:

module.exports = function(ignition) {
        console.log('Ignition Sequence Start: ' + ignition);
        return { Ignition: ignition };
    }

Now can can call it like this:

// Single method assigned directly inside module.exports
let exportFunctionDirectSingleResponse = exportFunctionDirectSingleMethod("LiftOff");
console.log('ExportFunctionDirect Response. ' + exportFunctionDirectSingleResponse.Ignition);

This method is not valid if there are more than one functions present in the file.

Method 4:

This way demonstrates how to call the constructor directly:

function ExportClass(incoming) {
    console.log('ExportClass invoked. Incoming: ' + incoming);
    this.name = incoming;
}

// Exporting this way, we can directly access the constructor
module.exports = ExportClass;

Now you can call the constructor directly using the new keyword:

const exportClassConstructor = require('./exportClassConstructor');

// using old-school class constructor invocation
let exportClass = new exportClassConstructor("The Boring Company");
console.log(exportClass.name);

Method 5:

At the end, we specify the most powerful way now available in javascript; the class syntax. You can define a class like this:

class TreasureIsland {
    constructor(){
        console.log('You have invoked the treasure island');
    }

    masterOfTreasures(incoming){
        return incoming;
    }

    set coast(coast) {
        this._coast = coast;
    }

    get coast(){
        return this._coast;
    }
}

module.exports = { TreasureIsland: TreasureIsland };

We can call it in the following way:

let exportClassComplete = require('./exportClassComplete').TreasureIsland;

var treasureIsland = new exportClassComplete();
treasureIsland.coast = 'Tortuga';
let coast = treasureIsland.coast;
console.log('Coast name is ' + coast);
let whoHasTheKey = treasureIsland.masterOfTreasures('Captain Barbarossa');
console.log('The master of the treasures is: ' + whoHasTheKey);

Notice how we require the module and then explicitly mention the class name to have it imported.

Apart from the class and the constructor, one important thing to note are the getter and setter methods for the variable coast using which we can directly set and get its value.

Difference between module.exports and exports

One last thing that needs to be mentioned is the difference between exports and module.exports. To put it very simply:

If you are exporting object(s), then the value exported by module.exports and exports is going to be the same. However, if the exported entity is a constructor function and is exported via exports, then the value of module.exports is going to be an empty object. As module.exports is the actual entity that is exported, the result in this case would be an empty object.

Now let’s explain the above statement. You can think of a module as an object and exports a mere property on that object. Like so:

let module = new Module();
let exports = module.exports;

Now when you export your module, module.exports is the return value and NOT exports.

Let’s export a couple of methods:

exports.first = function() {
    console.log("first");
}
exports.second = function() {
    console.log("second");
}

We have named the exported functions and thus they are added to module.exports as an object, like object : { first:[function], second:[function] }. This code is going to be exported perfectly without any issue, you can test it by the methods described above.

Now, on the other hand, things are not going to go smooth if we are to export a constructor function like this:

// Doesn't work
exports = function disasterLiftOff() {
    console.log('Total wreckage');
}

If we invoke this function like const liftOff = require('./liftOff')();, then we are going to have an error.

However, exporting this constructor using module.exports:

// Works
module.exports = function sweetLiftOff() {
    console.log('Perfect flight');
}

Works absolutely fine (invoked using the same code const liftOff = require('./liftOff')();)

So it really boils down to the fact that exports and module.exports are pointing at the same object or not, and in the case of constructor functions, it is just not the case.