Monday, January 9, 2012

Nodejs Modules and Export Explained


Enjoyed a week playing around with nodejs :) Lets share

Simple Node Server [http://localhost:6666]:

var http = require('http');

var server = http.createServer(function (req, res) {

           // Do Whatever you want

              res.writeHead(200, {'Content-Type':'text/plain'});

              res.end('Running');

         }).listen(6666, function () {

                   console.log('Node Runs on port 6666');

});

How to install a node_module?
NPM is a powerful package manager for node which you can use to install node modules
Ex:
npm install redis

The above command installs redis module in ./node_modules/redis directory

How to use a module?
Use require() method

Ex:
require('redis')

How will node resolve the modules?
Consider

/home/xxx/sample

 |___ index.js

 |___ node_modules

          |____ redis

                     |____lib/

                        ...

          |____ my_module

                        |____node_modules

                              |____first.js

                        |____test.js



//test.js

var redis = require('redis');


So node will look for redis module in

1.my_module/node_modules/
2.sample/node_modules/
3.xxx/node_modules/
4.home/node_modules
5./node_modules

Also some global folders

You can exploit this behaviour nicely if planned :)

Will node load the module everytime I request it to via require?
No, It won't load the module everytime you request untill the module requested resolves to a location different from previously loaded location.
Ya, Node caches the module

Cache means?? What will get cached?
uh uHHH... Lets get deeper into modules before this question

How to write your own module?
Simple... Let us write a module which performs lowercase to uppercase conversion

//simple.js

var stringtoUpper = function (text) {

        return text.toUpperCase();

};

exports.toUpper = stringtoUpper;

Done :)

//test.js

var utils = require('./simple');

console.log(utils.toUpper('this is text'));



//Execution

node test.js

>THIS IS TEXT


What is that exports?
That is whatever you wish to expose to the src module that requires the destination module.
This is shared between all instances of the current module.
exports.toUpper same and equal to module.exports.toUpper, [exports === module.exports] nice way to use. module is the referrence to the current module :)
Also you can name your export different from it's actual name. As you can see in the above example. Actual function's name is stringtoUpper, but exports' name is toUpper

Consider the same simple.js and instead exports.toUpper = stringtoUpper replace with

1.module.exports.toUpper = stringtoUpper;
2.module.exports = stringtoUpper;
3.module.exports = new stringtoUpper('this is a text');

All the three are different

module.exports is an object
1 makes it {toUpper: [Function]]}
2 makes it [Function] // We can use this a constructor function
3 makes it an object of stringtoUpper class

After require('test.js'); to use the exports, follow
1 : simple.toUpper('This is a text')
2 : simple('this is a text') or require('simple')('this is a text') or new simple('this is a text');
3 : You can access all public members. Here there is none

Methods 2 & 3 overrides the entire object, means whatever might be there initialised before via exports will be overridden
Ex:
module.exports.a = 10;
module.exports.b = 20;
module.exports = 20; // will override a & b
Hope you understand the reason. module.exports itself is an object and members of it can be initialised either as module.exports.a, module.exports.b but when module.exports = 20 happens the whole object is initialised with new value

Now lets comeback to caching

node caches all these exports of a module when they are loaded for the first time

1 & 2 has no effect on caching since they are just functions they can be called n number of times with change in parameter

But 3 is different. The object returned is cached. So how many ever time you load the module after first time, no instantiation take place [Kind of singleton]. Because you are exporting only one object of the class.

So, If you are looking for stateful implementation across modules you can go for 3, else go for 1 or 2

Happy Node :) Hope this helps :)