← back to articles

A Beginner Splurge in Node.js

Save article ToRead Archive Delete · Log out

9 min read · View original · sitepoint.com

This article was peer reviewed by Rabi Kiran. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

It’s 3 a.m., hands over the keyboard while staring at an empty console. The bright prompt over a dark backdrop ready, yearning to take in commands. Want to hack up Node.js for a little while? One of the exciting news about Node.js is that it runs anywhere. This opens up the stack to various ways to experiment with it. For any seasoned veteran, this is a fun run of the command line tooling. What I like the most is that we can survey the stack from within the safety net of the command line. The cool thing is that we are still talking about JavaScript, hence most of you should not have any problem. So, why not fire up node up in the console?

In this article, I’ll introduce you to Node.js. My goal is to go over the main highlights while hiking up some pretty high ground. This is an intermediate overview of the stack while keeping it all inside the console. If you want a beginner-friendly guide about Node.js, I suggest you to watch the SitePoint premium’s course Node.js: An Introduction.

Node.js in the Windows console

Why Node.js?

Before we begin, let’s go over the tidbits that make Node.js stand out from the crowd.

You may have heard these points through many sources, but what does it all mean? You can think of Node.js as the engine that exposes many APIs to the JavaScript language. In traditional computing where processes are synchronous, when you perform any I/O operation, the API waits before it runs the next line of code. An I/O operation is, for example, reading a file or making a network call. Node.js doesn’t do that and it’s designed from the beginning to have asynchronous operations. In today’s computing market, this has a tremendous advantage. Can you think of the last time you bought a new computer because it had a faster single processor? The number of cores and a faster hard-drive is more important.

In the remainder of the article, when you see a >, which is a prompt symbol, this means hit Enter to type up the next command. Moreover, before running the code in this article you have to open the CLI and execute the command node. With that said, let’s begin our tour!

Callbacks

To start, type up this function:

> function add(a, b, callback) { var result = a + b; callback(result); }

To a newbie, a callback in JavaScript may seem strange. It certainly doesn’t look like any classical OOP approach. In JavaScript, functions are objects and objects can take in other objects as parameters. JavaScript doesn’t care what an object has, so it follows that a function can take in an object that happens to be yet another function. The arity, which is the number of parameters, goes from two in add() to a single parameter in the callback. This system of callbacks is powerful since it enables encapsulation and implementation hiding. In Node.js, you’ll find a lot of APIs that take in a callback as a parameter. One way to think about callbacks is as a delegate. Programming lingo aside, a delegate is a person sent and authorized to represent others. So a callback is like sending someone to run an errand. Given a list of parameters, like a grocery list for example, they can go and do a task on their own.

To play around with add:

> add(2, 3, function (c) { console.log('2 + 3 = ' + c) });
> add(1, 1, function (c) { console.log('Is 1 + 1 = 3? ' + (c === 3)); });

Feel free to make up more creative ways to play around with callbacks. Callbacks are the building block for some important APIs in Node.js.

Asynchronous Operations

With callbacks, we are able to start building asynchronous APIs. For example:

> function doSomething (asyncCallback) { asyncCallback(); }
> doSomething(function () { console.log('This runs synchronously.'); });

This particular example has a synchronous execution. But, we have everything we need for asynchrony in JavaScript. The asyncCallback, for example, can get delayed in the same thread:

> function doSomething (asyncCallback) { setTimeout(asyncCallback, Math.random() + 1000); }
> doSomething(function () { console.log('This runs asynchronously.'); }); console.log('test');

I use a setTimeout to delay execution in the current thread. Timeouts do not guarantee time of execution. I placed a Math.random() to make it even more fickle. I call doSomething(), followed up with a console.log('test') to display delayed execution. You will experience a short delay between one to two seconds, then see a message pop up on the screen. What I illustrate is that asynchronous callbacks are unpredictable. Node.js places this callback in a scheduler and continues on its merry way. When the timer fires, Node.js picks up right where execution happens to be and calls the callback. So, you must wrap your mind around petulant callbacks to understand Node.js.

In short, callbacks are not always what they seem in JavaScript.

Let’s go on with something cooler. How about a simple DNS lookup in Node.js?

> dns.lookup('bing.com', function (err, address, family) { console.log(' Address: ' + address + ', Family: '  + family + ', Err: ' + err); });

The callback returns err, address, and family objects. What is important is that return values get passed in as parameters to the callback. So this is not like your traditional API of var result = fn('bing.com');. In Node.js, you must get callbacks and asynchrony to get the big picture. You are free to checkout the [DNS Node.js API][2] for more specifics. This is what the DNS lookup looks like in my console:

Node.js DNS lookup

File I/O

Now to pick up the pace, how about doing file I/O on Node.js? Imagine this scenario where you open a file, read it and then write content into it. In modern computer architecture, I/O bound operations lag. CPU registers are fast, the CPU cache is fast, RAM is fast. But, you go read and write to disk and it gets slow. So when a synchronous program performs I/O bound operations, it runs slowly. The better alternative is to do it asynchronous, as such:

> var fs = require('fs');
> fs.writeFile('message.txt', 'Hello Node.js', function () { console.log('Saved.'); }); console.log('Writing file...');

Due to the fact that the operation is asynchronous, you will see “Writing file…” before the file gets saved on disk. The natural use of callback functions fits well in this API. You can explore the [File System API][6] in the documentation. How about reading from this file? Can you guess off the top of your head how to do that in Node.js? I’ll give you a hint, the callback takes in err and data. I encourage you giving it a try.

Here is the answer:

> fs.readFile('message.txt', function(err, data) { console.log(data); });

You may also pass in an encoding option to get the utf-8 contents of the file:

> fs.readFile('message.txt', {encoding: 'utf-8'}, function(err, data) { console.log(data); });

The use of callback functions with async I/O looks nice in Node.js. The advantage here is we are leveraging a basic building block in JavaScript. Callbacks get lifted to a new level of pure awesomeness with asynchronous APIs that do not block.

A Web Server

So, how about a web server? Any good exposé of Node.js must run a web server. Imagine an API named createServer with a callback that takes in request and response. You can explore the HTTP API in the documentation. Can you think of what that looks like? You’ll need the http module. Go ahead and start typing in the console.

Here is the answer:

> var http = require('http');
> var server = http.createServer(function (request, response) { response.end('Hello Node.js'); });

When you think of the web, it is a client-server model of requests and responses. Node.js has a request object that comes from the client and a response object from the server. So the stack embraces the crux of the web with this simple callback mechanism. Do I need to remind you that it is asynchronous? I hope the pieces are coming together. If you look at the file API, what we are doing here is not so different. We bring in a module, tell it to do something and pass in a callback. The callback works like a delegate which does a specific task given a list of parameters.

Of course, everything is nonsense if we can’t it see it on a browser. To fix this type on the CLI:

server.listen(8080);

And point your favorite browser to localhost:8080, which in my case was Edge.

Node.js on Microsoft Edge

Imagine the request object as having a ton of information available to you. To rewire the server, let’s bring it down first:

> server.close();
> server = http.createServer(function (request, response) { response.end(request.headers['user-agent']); }); server.listen(8081);

Point the browser to localhost:8081. The headers object gives you user-agent information which comes from the browser. We can also loop through the headers object:

> server.close();
> server = http.createServer(function (request, response) { Object.keys(request.headers).forEach(function (key) { response.write(key + ': ' + request.headers[key] + ' '); }); response.end(); }); server.listen(8082);

Point the browser to localhost:8082 this time. Once finished playing around with your server, be sure to bring it down. The command line might start acting funny if you don’t.

> server.close();

So there you have it, creating web servers all through the command line. I hope you’ve enjoyed this psychedelic trip around node.

Conclusions

Node.js fits well in modern solutions because it’s simple and lightweight. It takes advantage of modern hardware with its non-blocking design. It embraces the client-server model which is intrinsic to the web. Best of all it runs JavaScript which is the language we love. What I find appealing is the crux of the stack is not so new. The web from its infancy got built around lightweight accessible modules. When you have time, I encourage you to read Tim Berners-Lees’ Design Principles. The principle of least power applies to Node.js given the choice to use JavaScript.

I hope you’ve enjoyed this look at the command line tooling, and bid you happy hacking.