Book Image

Node.js Blueprints

By : Krasimir Stefanov Tsonev
Book Image

Node.js Blueprints

By: Krasimir Stefanov Tsonev

Overview of this book

Table of Contents (19 chapters)
Node.js Blueprints
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Organizing your code logic in modules


If we write a lot of code, sooner or later, we will start realizing that our logic should be split into different modules. In most languages, this is done through classes, packages, or some other language-specific syntax. However, in JavaScript, we don't have classes natively. Everything is an object, and in practice, objects inherit other objects. There are several ways to achieve object-oriented programming within JavaScript. You can use prototype inheritance, object literals, or play with function calls. Thankfully, Node.js has a standardized way of defining modules. This is approached by implementing CommonJS, which is a project that specifies an ecosystem for JavaScript.

So, you have some logic, and you want to encapsulate it by providing useful API methods. If you reach that moment, you are definitely in the right direction. This is really important, and maybe it is one of the most challenging aspects of programming nowadays. The ability to split our applications into different parts and delegate functions to them is not always an easy task. Very often, this is undervalued, but it's the key to good architecture. If a module contains a lot of dependencies, operates with different data storages, or has several responsibilities, then we are doing something wrong. Such code cannot be tested and is difficult to maintain. Even if we take care about these two things, it is still difficult to extend the code and continue working with it. That's why it's good to define different modules for different functionalities. In the context of Node.js, this is done via the exports keyword, which is a reference to module.exports.

Building a car construction application

Let's elucidate the process with a simple example. Assume that we are building an application that constructs a car. We need one main module (car) and a few other modules, which are responsible for the different parts of the car (wheels, windows, doors, and so on). Let's start with the definition of a module representing the wheels of the car, with the following code:

// wheels.js
var typeOfTires;
exports.init = function(type) {
    typeOfTires = type;
}
exports.info = function() {
  console.log("The car uses " + typeOfTires + " tires.");
}

The preceding code could be the content of wheels.js. It contains two methods. The first method, init, should be called first and accepts one setting, that is, the type of the wheels' tires. The second method simply outputs some information. In our main file, car.js, we have to get an instance of the wheels and use the provided API methods. This can be done as follows:

// cars.js
  var wheels = require("./wheels.js");
  wheels.init("winter");
  wheels.info();

When you run the application with node car.js, you will get the following output:

The car uses winter tires.

So, everything that you want to expose to the outside world should be attached to the export object. Note that typeOfTires is a local variable for the module. It is available only in wheels.js and not in car.js. It's also a common practice to apply an object or a function to the exports object directly, as shown in the following code for example:

// engine.js
var Class = function() {
    // ...
}
Class.prototype = {
  forward: function() {
    console.log("The car is moving forward.");
  },
  backward: function() {
    console.log("The car is moving backward.");	
  } 
}
module.exports = Class;

In JavaScript, everything is an object and that object has a prototype property. It's like a storage that keeps the available variables and methods. The prototype property is heavily used during inheritance in JavaScript, because it provides a mechanism for transferring logic.

We will also clear the difference between module.exports and exports. As you can see, in wheels.js, we assigned two functions, init and info, directly to the exports global object. In fact, that object is a reference to module.exports, and every function or variable attached to it is available to the outside world. However, if we assign a new object or function directly to the export object, we should not expect to get an access to it after requiring the file. This should be done with module.exports. Let's take the following code as an example:

// file.js
module.exports.a = 10;
exports.b = 20;

// app.js
var file = require('./file');
console.log(file.a, file.b);

Let's say that both the files, app.js and file.js, are in the same directory. If we run node app.js, we will get 10 20 as the result. However, consider what would happen if we changed the code of file.js to the following code:

module.exports = { a: 10 };
exports.b = 20;

Then, in this case, we would get 10 undefined as the result. That's because module.exports has a new object assigned and exports still points to the old one.

Using the car's engine

Let's say that the module in engine.js controls the car. It has methods for moving the car forward and backward. It is a little different because the logic is defined in a separate class and that class is directly passed as a value of module.exports. In addition, as we are exporting a function, and not just an object, our instance should be created with the new keyword. We will see how the car's engine works with the new keyword as shown in the following code:

var Engine = require("./engine.js");
var e = new Engine();
e.forward();

There is a significant difference between using JavaScript functions as constructors and calling them directly. When we call the function as a constructor, we get a new object with its own prototype. If we miss the new keyword, the value which we get at the end is the result of the function's invocation.

Node.js caches the modules returned by the require method. It's done to prevent the blocking of the event loop and increase the performance. It's a synchronous operation, and if there is no cache, Node.js will have to do the same job repeatedly. It's also good to know that we can call the method with just a folder name, but there should be a package.json or an index.js file inside the directory. All these mechanisms are described well in the official documentation of Node.js at http://nodejs.org/. What is important to note here is that the environment encourages modular programming. All we need is native implementation into the system, and we don't have to use a third-party solution that provides modularity.

Like in the client-side code, every Node.js module can be extended. Again, as we are writing the code in plain JavaScript, we can use the well-known approaches for inheritance. For example, take a look at the following code:

var Class = function() { }
Class.prototype = new require('./engine.js')();
Class.prototype.constructor = Class;

Node.js even offers a helper method for this purpose. Let's say that we want to extend our engine.js class and add API methods to move the car in the left and right directions. We can do this with the following piece of code:

// control.js
var util = require("util");
var Engine = require("./engine.js");
var Class = function() { }
util.inherits(Class, Engine); 
Class.prototype.left = function() {
  console.log("The car is moving to left.");
};
Class.prototype.right = function() {
  console.log("The car is moving to right.");  
}
module.exports = Class;

The first line gets a reference to the Node.js native utils module. It's full of useful functions. The fourth line is where the magic happens. By calling the inherits method, we have actually set a new prototype of our Class object. Keep in mind that every new method should use the already applied prototype. That's why the left and right methods are defined after the inheritance. At the end, our car will move in four directions, as shown in the following code snippet:

var Control = require("./control.js");
var c = new Control();
c.forward();
c.right();