4 Feb
2011

Taking Baby Steps with Node.js – CommonJS and Creating Custom Modules

Category:UncategorizedTag: , :

Here are the links to the previous installments:

  1. Introduction
  2. Threads vs. Events
  3. Using Non-Standard Modules
  4. Debugging with node-inspector

In a previous blog post, I already discussed how to make use of the built-in and third-party modules inside a Node.js application. For this post I?m going to briefly touch on CommonJS and show how to create custom modules.

Most programming languages out there, like Java, Ruby, Python, C#, C++, etc ?,  all come with some sort of standard library that provides developers with an API for building all kinds of applications on a variety of platforms. These libraries and/or frameworks provide all kinds of basic functionality for accessing the file system, doing network I/O, parsing command-line arguments, etc. ? . Unfortunately, JavaScript doesn?t come with such a standard library. This is something that the CommonJS initiative is trying to fix. CommonJS tries to go beyond the standard JavaScript specification by defining a common set of API?s for building a broad range of systems like command-line, server-side and GUI applications. If you?re interested, you can have a look at the current specifications and proposals in development.

What does this have to do with Node.js? Well, Node.js implements the CommonJS specification for its built-in modules. Knowing how to leverage your own custom modules is not only very important for structuring your Node.js applications but also recommended for providing portability with other CommonJS compliant frameworks like narwhal. Creating a custom module is very easy. We just have to provide a JavaScript file, name it after the module that we want create and add the necessary JavaScript code. That?s it!

Let?s look at a very simple example of how to build such a custom module using the CommonJS system provided by Node.js. Suppose that we want to create a module named podcast that exposes functionality for downloading .mp3 files. As mentioned earlier, we have to create a JavaScript file named podcast.js and add the necessary JavaScript code that provides the download functionality.

exports.download = function(episode) {
    console.log('Downloading: ' + episode);
}

A slight variation to this that I see quite often used both in the built-in as the third-party modules looks like the following:

var podcast = exports;

podcast.download = function(episode) {
    console.log('Downloading: ' + episode);
}

In order to use this exciting new piece of code, we just have to add a require statement to our client code and we?re good to go:

var podcast = require("./podcast");
podcast.download('Astronomy podcast #89');

This might look very simple and easy but there?s plenty going on behind the scenes. First of all, Node.js ensures that the content of the JavaScript file that makes up our custom module gets loaded into its own scope. By doing this, Node.js automatically prevents naming collisions with other modules. When loading our custom module, Node.js provides a number of objects like module, exports and require. These are also called pseudo globals.

We use the exports object for exposing public members to external code. You can consider exports as the this reference for our module. This also means that we can just add regular JavaScript functions to our custom module without adding them to the global namespace and without the external code being able to call these ?private? functions as long as we don?t add them to the exports object.

var podcast = exports;

podcast.download = function(episode) {
    downloadDataFor(episode);
}

function downloadDataFor(episode) {
    console.log('Downloading: ' + episode);    
}

The external code is not able to call the downloadDataFor function which is only available inside our custom module:

var podcast = require("./podcast");
podcast.download('Astronomy podcast #89');

console.log(typeof podcast.download);            // function
console.log(typeof podcast.downloadDataFor);    // undefined
console.log(typeof downloadDataFor);            // undefined

I just mentioned that a module is provided with a couple of pseudo global objects. One of these is named module which provides a reference to the current instance of the module. This means that we are able to replace the reference that is hold by the exports property of the current module in order to provide a single export per JavaScript file. Let?s talk code: 

function Podcast() {
    if(false === (this instanceof Podcast)) {
        return new Podcast();
    }
}

Podcast.prototype.download = function(episode) {
    console.log('Downloading: ' + episode);    
}

module.exports = Podcast;

We now have to use restructure the client code as well:

var Podcast = require("./podcast");

var astronomyCast = new Podcast();
astronomyCast.download('Astronomy podcast #89');

The advantage of this approach is that both the code of our custom module as well as the client code are organized in the same way as we would write regular JavaScript objects. 

I can only hope that the CommonJS initiative succeeds in its goals by providing a common set of API?s that can be used for building a wide range of applications using JavaScript. Using modules interchangeably on all kinds of platforms still sounds very appealing :-).

Until next time.