Welcome back! In my last post we’ve looked at Node.js and added in some Express. You might think that’s that for Node.js, but actually there is just more Node.js stuff I’d like to talk about. So what do I have in store for you this week? We’ll be looking at modules and events, two important aspects of Node.js.
If you haven’t read my previous articles on MEAN and Node.js yet I strongly advice to read them now as this post continues where the previous one left off (although we won’t be using Express in this post).
- MEAN web development #1: MEAN, the what and why
- MEAN web development #2: Node.js in the back
- MEAN web development #3: More Node.js
- MEAN web development #4: All aboard the Node.js Express!
- MEAN web development #5: Jade and Express
- MEAN web development #6: AngularJS in the front
- MEAN web development #7: MongoDB and Mongoose
- MEAN web development #8: Sockets will rock your socks!
- MEAN web development #9: Some last remarks
As always you can find the sources for this blog on my GitHub account in the mean3-blog repository.
And be sure to follow me on Twitter @sanderrossel for more content related to MEAN, Node.js, web development and software development in general.
Modules
In my previous post you’ve already had a brief introduction to modules. Every time you use a require(‘http’) you’re loading the http module. There’s lots of modules, like http, https, net (for socket programming), mongodb, express and just a whole lot more (thousands!). Some come with Node.js, like http, and some need to be added, either manually or through a package manager like npm, like mongodb (which we’ll use in the ‘M post’ of this series!).
Why would you want to create modules? Think of it as a package, a piece of code that is reusable across files and projects. Let’s say you write an awesome bit of code, or just some code you need a lot. You can put this in a module and use it everywhere you like. You can even distribute it (to GitHub, npm, Bower…) so others can use it too!
So let’s build a module. You won’t believe how easy it is! Create a file, myModule.js, and place it in the same folder as your Node.js application. Now in myModule.js simply put the following JavaScript.
exports.hello = "Hello, module!";
Now in your main Node.js application you can use it as follows.
var myModule = require('./myModule.js');
console.log(myModule.hello);
Can you believe it’s that easy!? I’m not entirely happy though. I’m requiring a file in a location. The ./ indicates the folder in which the Node.js file that did the require is located. When we call http, a native module, we don’t need to specify a path though. Alright, but that’s native you might say. But when requiring Express, as we’ve done in my previous post, we didn’t need to specify a path either! Well here’s the thing, when you don’t specify a path Node.js will look for a file with the specified name in the node_modules folder. So if it doesn’t already exist create a folder with the name node_modules and put your myModule.js in it. You can now load it using the following syntax.
var myModule = require('myModule');
That looks better! However, if you’ve installed Express you’ll see that Express has a folder inside the node_modules folder with other folders and files in it. That’s what I want too! Luckily that’s not hard to do either. First we create a folder inside node_modules called myModule. Then we can either create a package.json and put the following JSON in it.
{ "name" : "myModule",
"main" : "./node_modules/myModule/myModule.js" }
Or we can rename myModule.js to index.js and require will try to get the index.js (or index.node) from node_modules/parameter_passed_to_require/index.js.
So let’s sum that up real quick. Require tries to get the file you passed in as a parameter or, if you didn’t specify the path, it will try to load ./node_modules/parameter_passed_to_require.js or ./node_modules/parameter_passed_to_require/index.js where ./ is the folder of the file that does the require.
Exports
So we can now create the simplest of module and load it using require. But we don’t really understand what’s going on yet. Where did this ‘exports’ thing come from and how can we utilize it?
When using require to get a module require will actually provide you with the exports variable. It’s shorthand for module.exports, so you may use that too. require will simply return the exports object. Basically, when writing a module, imagine the following lines of JavaScript at the top of the file.
var module = {
exports: {}
};
var exports = module.exports;
So now we can simply use exports and append all kinds of properties and functions to it. For example, let’s create a user module and use it in our application. You can find the source code for this on GitHub.
exports.firstName = null;
exports.lastName = null;
exports.birthDate = null;
exports.getFullName = function() {
return exports.firstName + ' ' + exports.lastName;
};
exports.getAge = function() {
return new Number((new Date().getTime() - exports.birthDate.getTime()) / 31536000000).toFixed(0);
};
And now we can use it like this.
var http = require('http');
var user = require('user_export');
user.firstName = 'Sander';
user.lastName = 'Rossel';
user.birthDate = new Date(1987, 11, 8);
var server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(user.getFullName() + ' is ' + user.getAge() + ' years old.');
});
server.listen(1337, '127.0.0.1');
So that’s awesome! But let’s add another user. We might go about it as follows.
var http = require('http');
var user1 = require('user_export');
var user2 = require('user_export');
user1.firstName = 'Sander';
user1.lastName = 'Rossel';
user1.birthDate = new Date(1987, 11, 8);
user2.firstName = 'Brendan';
user2.lastName = 'Eich';
user2.birthDate = new Date(1961, 1, 1);
var server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(user1.getFullName() + ' is ' + user1.getAge() + ' years old.' +
'\n' + user2.getFullName() + ' is ' + user2.getAge() + ' years old.');
});
server.listen(1337, '127.0.0.1');
Now if you run this you’ll notice that this is completely wrong! We can’t have more than one instance of user, so now both users are actually Brendan Eich!
So our module is not very re-usable… But remember that I said that require will just return the exports object? How about if we just set the exports object to a constructor for our user object? The module would now look like this.
module.exports = function User(firstName, lastName, birthDate) {
var self = this;
self.firstName = firstName;
self.lastName = lastName;
self.birthDate = birthDate;
self.getFullName = function() {
return self.firstName + ' ' + self.lastName;
};
self.getAge = function() {
return new Number((new Date().getTime() - self.birthDate.getTime()) / 31536000000).toFixed(0);
};
};
Notice that I’m using module.exports! Setting the exports shorthand wouldn’t work here as it is module.exports that is loaded by require! exports is just a shorthand. It references the same object as module.exports, but it is not module.exports. This is a very important difference. If you’re not sure what to use just use module.exports and you’re safe.
So we’re now loading a function with require. How does that look?
var http = require('http');
var user = require('user');
var user1 = new user('Sander', 'Rossel', new Date(1987, 11, 8));
var user2 = new user('Brendan', 'Eich', new Date(1961, 1, 1));
var server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(user1.getFullName() + ' is ' + user1.getAge() + ' years old.' +
'\n' + user2.getFullName() + ' is ' + user2.getAge() + ' years old.');
});
server.listen(1337, '127.0.0.1');
The code became a little cleaner, but more importantly, I can now have two instances of the user object!
And of course you can require in modules too. Try putting the following in a module.
var http = require('http');
exports.startServer = function() {
var server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Server was started from module!');
});
server.listen(1337, '127.0.0.1');
}
And to try that out.
require('start_server').startServer();
Events
Another important aspect of Node.js is Events. If you’ve worked with .NET, and especially WinForms, you should be familiar with events already. In JavaScript events are usually implemented as callbacks, a function that is called when a certain action completes. Node.js has a special module that handles events.
Let’s implement a basic event.
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
emitter.on('request', function(url) {
console.log('requested ' + url);
});
var server = http.createServer(function (req, res) {
emitter.emit('request', req.url);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('url logged to console.');
});
server.listen(1337, '127.0.0.1');
So as you can see we start by requiring the events module. We then create a new instance of the EventEmitter class. To create an event simply use the on method and pass it the name of the event and a function that should be executed when the event triggers. To actually trigger the event call the emit method and pass it the name of the event and the event data.
It is totally acceptable to add multiple handlers to the same event.
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
emitter.on('request', function(url) {
console.log('requested ' + url);
});
emitter.on('request', function(url) {
console.log('second listener, received request ' + url);
});
var server = http.createServer(function (req, res) {
emitter.emit('request', req.url);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('url logged to console.');
});
server.listen(1337, '127.0.0.1');
It’s also possible to handle an event only once using the once method.
emitter.once('request', function(url) {
console.log('one time listener, received request ' + url);
});
And you can remove listeners as well using the removeListener function. To remove a listener you need to pass in the listening function, so we cannot start listening using an anonymous function like I did up til now. So in the following example I declare a function, add it to my emitter and then remove it after three server requests.
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
var removableEvent = function(url, counter) {
console.log('this event will be removed in ' + counter + '...');
};
emitter.on('request', removableEvent);
var counter = 3;
var server = http.createServer(function (req, res) {
emitter.emit('request', req.url, counter);
counter--;
if (counter === 0) {
emitter.removeListener('request', removableEvent);
};
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('url logged to console.');
});
server.listen(1337, '127.0.0.1');
And of course any event emitter can have as many events as you want and trigger them in different places.
var http = require('http');
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
emitter.on('request', function(url) {
console.log('requested ' + url);
});
emitter.on('message', function(message) {
console.log('Received message: ' + message);
});
var server = http.createServer(function (req, res) {
emitter.emit('request', req.url, counter);
emitter.emit('message', 'Received request for ' + req.url);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('url logged to console.');
});
server.listen(1337, '127.0.0.1');
emitter.emit('message', 'Server started and listening on 127.0.0.1:1337');
So as you see working with events isn’t very difficult, you just have to know how it works.
This was my third article in the MEAN series and the second one on Node.js. There’s a lot to say about Node.js, in fact I’m not quite done yet! We’ll look at more Node.js goodness in upcoming articles. I do want to move on to the other letters of MEAN as well though. So for my next article I’m going for either M (MongoDB) or E (Express.js). Both require more Node.js. So if you have any preference let me know! M or E, most votes wins. For no votes or a tie I’m simply going to pick one myself.
That’s it for this week. I hope to see you back next week for either MongoDB or Express.js (be sure to let me know what you want!).
Stay tuned!
The post MEAN web development #3: More Node.js appeared first on Sander's bits.