NodeJS Introduction: Part 1
29th May 2011
A few weeks ago I attended Remy Sharp’s NodeJS Workshop in Nottingham which was a day of hanging out with some awesome people and getting stuck in to NodeJS. This is my attempt to write up what I learnt in the hope that some of it actually sticks in my head, so apologies for any poor grammar; this really is more a note-to-self rather than something other humans should be reading.
Installation on Ubuntu 10.10 Server Edition
You can do
sudo apt-get install node
But where is the fun in that right? (Also you probably won’t be getting the latest version) Let’s build it ourselves.
Make sure dependencies are covered:
sudo apt-get install libssl-dev
Node also requires Python but that should already be installed by default with Ubuntu
The installation instructions on the Node Wiki are good but we are going to differ slightly to where we install node. Follow along with me: from your home dir
mkdir src/
cd src
git clone https://github.com/joyent/node.git
cd node
./configure --prefix=$HOME/local/node
make && make install
This was as per the wiki. Now here we go our own way. Ubuntu automatically adds our ~/bin folder to the $PATH so if we install node there then we won’t have to bother about setting the Path variables later.
mkdir ~/bin
cd bin
ln -s ~/local/node/bin/node
For those of you who like to know WHY this works, have a look at
cat ~/.profile
where you’ll see that ~/bin path being added.
Now
node -v
should tell you the version. If you get a program not installed message try:
source ~/.profile
and then try again.
Install NPM
Because Node Package Manager installs stuff on your machine, it is recommended to run it under sudo so that NPM can change the user to ‘nobody’ before running any build commands. As the installation docs say: “npm will downgrade permissions if it's root before running any build scripts that package authors specified.”
sudo curl http://npmjs.org/install.sh | clean=yes sh
Now doing
npm -v
should give you the version number
Our first script
Yes, we are going to do a Hello World example, but this will show the event-driven non-blocking nature of NodeJS
console.log('Hello');
setTimeout(function(){
console.log('World')
}, 2000);
console.log('JavaScript');
Because NodeJS is build on top of Google’s V8 Javascript Engine which is used in Chrome amongst many other places, the calls are much the same as client-side JavaScript. So console.log will output stuff to the screen, in this case since NodeJS is run from the command line it is STDOUT. Run the script
node hello-world.js
which will output
Hello
JavaScript
World
Look at the setTimeout function; this says in 2000 milliseconds time (2 seconds to you and me) call console.log(). If you were to write something like this in PHP
echo ‘Hello’;
sleep(2000);
echo ‘PHP’;
echo ‘world’;
So PHP is not event-driven, you’ll see that while the sleep function is invoked, nothing happens; no processing occurs. Compare that with JavaScript where the console.log('JavaScript'); is executed even while the console.log('World'); function is waiting to be executed.
There is a util module that adds more logging options
var util = require('util');
//util log adds a timestamp
util.log('foo');
// util.print doesnt add line breaks
util.print('foo');
console.log('foo');
// console.dir can output object memebers
console.dir(global);
// can direct errors to STDERR
console.error('echoed on STDERR');
More command line utilities
Node’s Process global object is useful for command line tools, and does things such as give us access to the arguments passed via the command line
process.stdout.write('hello world!\n');
console.log(process.argv[2]);
So try that out with
node command-line.js Hi
Process has access to environment vars which can be set when calling a script. This provides a nice way to start a server based on whether we want it set up for production or development. e.g:
$ NODE_ENV=production node lanch-server.js
And in launch-server.js:
var port = 8000;
if(process.env.NODE_ENV == 'production'){ port = 80;}
server.listen(port);
Here is a list of functions available via Process
Loading modules
Node loads modules using the require(‘module-name) function. To load the path module we can
var path = require(‘path’);
The argument we pass to the require function is either the name of the module that Node has already been installed and knows about. Or we can pass in a path for a module of our own. Node looks in folders in a certain order
Path Module
The Path module is used for dealing with file paths. This example will demonstrate using a callback function to alert us as to whether a file exists or not.
var path = require('path');
console.log('path exists?');
path.exists(process.argv[2], function(exists){
console.log('files exists: ' + (exists ? 'yes' : 'no'));
});
console.log('waiting...');
From the command line:
$ node callback-path.js test.txt
Webservers
So that is all well and good but the fun really starts when we use NodeJS for web servers. Node has an http module
Here is out hello world server example
var http = require('http'),
var server = http.createServer(function(req, res){
res.writeHead(200, {'content-type' : 'text/html'});
//res.write is optional
res.write('<p>Hello</p>');
res.end('<p> World</p>');
});
server.listen(8000);
The salient parts of that example are: http module has a createServer() function that accepts an annoymous function with params request and respsonse. We can use the response object to write the HTTP headers with a response code (200 OK) and passin an object literal with other options, in this case setting the mime type. There is a write function that writes the output but keeps the connection alive. The end function outputs whatever is passed to it and then closes the connection. For more on how NodeJS keeps connections open look at chunked encoding. We then start the server listening on port 8000. As we know, higher port numbers can be opened by any user, ports lower than 1023 can only be opened by the root user.
start the script
$ node simple.js
which will lock your command prompt. Stop the server by ctrl + c.
Because the application code is in the same file as the server code, if you want to change the code then you edit the file and then you have stop the server and then restart it. This is tiresome. Use nodemon which will restart NodeJS when it detects a change in the files.
Install nodemon:
$ npm install nodemon -g
The -g flag tells npm to install it globally which is necessary as nodemon is command line utility.
Now start Node by
$ nodemon simple.js
You’ll see your page as before at localhost:8000, then go and edit the simple.js file and look at your terminal and you’ll see nodemon detect the change and then restart. Boom! Nodemon has lots of awesome features such as being able to ignore changes in certain file types; read more about it here
ConnectJS
So we have got a basic server working, what other cool shit can we do? ConnectJS is a middleware library for NodeJS.
What the hell is Middleware? From my Java days I thought of Corba and promptly broke out in hives. Luckily, it’s not that.
This is a better explanation of middleware
Connect allows you to register certain actions withNodeJS and then based on the type of request Node will respond appropriately. OK, that is not a great explanation so let’s try and example.
var connect = require('connect'),
path = require('path'),
parse = require('url').parse,
querystring = require('querystring');
var server = connect.createServer(
function(req, res, next){
if(req.url.indexOf('favicon') === -1){
count++;
req.count = count;
}
next();
},
connect.bodyParser(),
connect.router(route),
connect.static(__dirname + '/public')
);
server.listen(8000);
The thing to note here is that instead of using the http module to create the server we use connect. We can then pass in any number of functions that will get called between the request being cand the response returned. In this case there is a function that counts the number of times the page has been loaded, bodyParser which gives us access to the querystring (CHECK), then we set up routes that match a requested URL and perform a specific action and finally we tell Connect which folder to use to serve out static files form. If one function in the middleware has finished processing, or we want to force control back to connnect, we can use the next() method which will return control to Connect.
Coming up in part 2, just as soon as I've typed it: ExpressJS, Jade and EJS, Websockets and event-source...
Tagged: Javascript
Comments and corrections to @edvanbeinum