Defining and using modules in node.js

JavaScript as a language does not have mechanisms to define real classes. In fact, everything in JavaScript is an object. We normally inherit properties and functions from one object to another. Thankfully, Node.js adopts the concepts defined by CommonJS—a project that specifies an ecosystem for JavaScript.

We encapsulate logic in modules. Every module is defined in its own file. Let’s illustrate how everything works with a simple example. Let’s say that we have a module that represents this site and we save it in a file called dunebook.js

// dunebook.js
exports.name = 'Node.js by example';
exports.read = function() {
   console.log('I am reading ' + exports.name);
}

We defined a public property and a public function. Now, we will use require to access them:

// script.js
var book = require('./dunebook.js');
console.log('Name: ' + dunebook.name);
book.read();

We will now create another file named script.js. To test our code, we will run node ./script.js. The result in the terminal looks like this:

Along with exports, we also have module.exports available. There is a difference between the two. Look at the following pseudocode. It illustrates how Node.js constructs our modules:

var module = { exports: {} };
var exports = module.exports;
// our code
return module.exports;

So, in the end, module.exports is returned and this is what require produces. We should be careful because if at some point we apply a value directly to exports or module.exports, we may not receive what we need. Like at the end of the following snippet, we set a function as a value and that function is exposed to the outside world:

exports.name = 'Node.js by example';
exports.read = function() {
   console.log('Iam reading ' + exports.name);
}
module.exports = function() {  ... }

In this case, we do not have an access to .name and .read. If we try to execute node ./script.js again, we will get the error output:

To avoid such issues, we should stick to one of the two options—exports or module.exports—but make sure that we do not have both.

Recommended :  Understand messaging system In Nodejs

 

We should also keep in mind that by default, require caches the object that is returned. So, if we need two different instances, we should export a function. Here is a version of the book class that provides API methods to rate the books and that do not work properly:

// dunebook.js
var ratePoints = 0;
exports.rate = function(points) {
   ratePoints = points;
}
exports.getPoints = function() {
   return ratePoints;
}

Let’s create two instances and rate the books with different points value:

// script.js
var bookA = require('./dunebook.js');
var bookB = require('./dunebook.js');
bookA.rate(10);
bookB.rate(20);
console.log(bookA.getPoints(), bookB.getPoints());

The logical response should be 10 20, but we got 20 20. This is why it is a common practice to export a function that produces a different object every time:

// book.js
module.exports = function() {
   var ratePoints = 0;
   return {
      rate: function(points) {
         ratePoints = points;
      },
      getPoints: function() {
         return ratePoints;
      }
   }
}

Now, we should also have require('./dunebook.js')() because require returns a function and not an object anymore.

Recommended :  Typescript vs ECMAScript which One is Better for writing JavaScript

 

About the author

Deven Rathore

I'm Deven Rathore, a multidisciplinary & self-taught designer with 3 years of experience. I'm passionate about technology, music, coffee, traveling and everything visually stimulating. Constantly learning and experiencing new things.

Pin It on Pinterest

Shares