Find file
Fetching contributors…
Cannot retrieve contributors at this time
3101 lines (2874 sloc) 101 KB
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="prettify/prettify.css" type="text/css" rel="stylesheet" />
<title>The Node Beginner Book » A comprehensive Node.js tutorial</title>
<meta name="description" content="A comprehensive Node.js tutorial for beginners: Learn how to build a full blown web application with server-side JavaScript" />
<style type="text/css">
body {
font-family: Georgia, serif;
background-color: #eee;
padding-top: 0px;
-webkit-text-size-adjust: auto;
}
#book, #donate, #disqus_thread, #footer, #workinprogressnote {
width: 640px;
margin: 0 auto;
margin-top: 24px;
margin-bottom: 100px;
padding: 64px;
background-color: white;
border-top: 1px solid #ddd;
border-left: 1px solid #ddd;
border-bottom: 1px solid #ddd;
border-right: 1px solid #ddd;
z-index: 100;
-moz-box-shadow: 14px 11px 27px #888;
-webkit-box-shadow: 14px 11px 27px #888;
box-shadow: 14px 11px 27px #888;
}
#book {
margin-top: 124px;
}
#author {
margin-top: -41px;
margin-left: 279px;
color: #888;
font-family: "Helvetica Neue", sans-serif;
font-size: 75%;
}
#updateinfo {
margin-top: -1px;
margin-left: 279px;
color: #bbb;
font-family: "Helvetica Neue", sans-serif;
font-size: 75%;
}
#workinprogressnote p {
font-family: "Helvetica Neue", sans-serif;
color: #700;
text-align: center;
}
#buy-the-ebook, #translations {
font-family: "Helvetica Neue", sans-serif;
font-size: 13px;
color: #fff;
text-align: center;
padding: 8px;
padding-left: 32px;
padding-right: 32px;
border-radius: 8px;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
box-shadow: 0px 0px 4px #444;
-moz-box-shadow: 0px 0px 4px #444;
-webkit-box-shadow: 0px 0px 4px #444;
position: relative;
margin-bottom: 20px;
margin-left: auto;
margin-right: auto;
width: 704px;
background-color: #555;
}
#translations a {
text-decoration: none;
color: #fff;
}
#translations .flag {
display: inline-block;
vertical-align: middle;
padding-top: 4px;
}
#translations .text {
display: inline-block;
vertical-align: middle;
margin-right: 24px;
}
#buy-the-ebook {
padding-bottom: 20px;
}
#buy-the-ebook .cover {
width: 131px;
display: table-cell;
padding-right: 0px;
padding-left: 16px;
vertical-align: top;
align: center;
color: #bbb;
}
#buy-the-ebook .cover img {
border: 1px solid black;
box-shadow: 0 1px 2px rgba(0,0,0,0.4), inset -1px 1px 0 rgba(255,255,255,0.4);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,0.4), inset -1px 1px 0 rgba(255,255,255,0.4);
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.4), inset -1px 1px 0 rgba(255,255,255,0.4);
}
#buy-the-ebook .description {
width: 349px;
display: table-cell;
padding-right: 16px;
padding-left: 16px;
vertical-align: top;
text-align: center;
}
#buy-the-ebook strong.price {
font-size: 18pt;
color: #14d014;
}
#buy-the-ebook strong.price.dollarsign {
font-weight: normal;
}
#buy-the-ebook .buy {
display: table-cell;
vertical-align: top;
color: #bbb;
}
.button {
padding: 8px;
margin-top: 26px;
width: 140px;
border: 1px solid #444;
border-radius: 6px;
background-image: -moz-linear-gradient(top, #14d014, #029302);
background-image: -webkit-gradient(linear, center top, center bottom, from(#14d014), to(#029302));
box-shadow: 0 1px 2px rgba(0,0,0,0.4), inset -1px 1px 0 rgba(255,255,255,0.4);
-moz-box-shadow: 0 1px 2px rgba(0,0,0,0.4), inset -1px 1px 0 rgba(255,255,255,0.4);
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.4), inset -1px 1px 0 rgba(255,255,255,0.4);
}
.buttonlink {
text-decoration: none;
font-weight: bold;
font-size: 15px;
color: #fff;
text-shadow: 0.1em 0.1em #666;
}
#donate p {
font-family: "Helvetica Neue", sans-serif;
text-align: center;
}
#flattr {
padding-top: 20px;
margin-left: 183px;
margin-right: 18px;
text-align: center;
align: left;
display: inline-block;
width: 100px;
}
#paypal {
display: inline-block;
margin-top: 24px;
text-align: center;
vertical-align: top;
}
#paypal fieldset {
border: 0;
}
#disqus_thread {
font-family: "Helvetica Neue", sans-serif;
}
#footer {
font-family: "Helvetica Neue", sans-serif;
background-color: #f5f5f5;
margin-top: 100px;
}
#footer p {
text-align: center;
font-family: "Helvetica Neue", sans-serif;
font-size: 11px;
}
#ccimage {
float: left;
margin-left: 30px;
margin-right: 0;
margin-top: 20px;
}
#praise {
position: absolute;
top: 120px;
right: 40px;
width: 204px;
font-style: italic;
color: #aaa;
z-index: -1;
text-align: right;
}
#praise .praise {
margin-bottom: 25px;
}
#praise .author {
font-style: normal;
font-size: 75%;
}
#table-of-contents-headline {
margin-top: 48px;
color: #700;
font-size: 160%;
font-weight: bold;
}
#book #table-of-contents {
margin-left: -24px;
}
#book #table-of-contents ul li {
font-size: 100%;
margin-left: 0;
list-style-type: none;
}
#book #table-of-contents ul {
padding-left: 24px;
margin-top: 12px;
margin-left: 0;
margin-bottom: 36px;
}
h1 {
margin-left: -63px;
margin-top: -109px;
font-size: 300%;
color: #700;
font-style: italic;
font-weight: bold;
-webkit-text-size-adjust: 100%;
}
h2 {
margin-top: 64px;
font-size: 180%;
}
h3 {
font-size: 160%;
}
h4 {
font-size: 140%;
}
h5 {
font-size: 120%;
}
h3, h4, h5 {
margin-top: 36px;
}
h2, h3, h4, h5 {
color: #700;
font-weight: bold;
margin-bottom: 36px;
}
#book p {
text-align: justify;
font-size: 110%;
line-height: 150%;
margin-bottom: 48px;
margin-top: -22px;
}
pre {
background-color: #f7f7f7;
border: 1px solid #eee;
padding: 16px;
margin-bottom: 64px;
margin-top: -24px;
font-size: 14px;
}
pre.prettyprint {
background-color: #f7f7f7;
border: 1px solid #eee;
padding: 16px;
margin-bottom: 64px;
margin-top: -24px;
font-size: 14px;
}
#book ul {
margin-top: -24px;
margin-bottom: 64px;
}
ul li {
margin-bottom: 12px;
font-size: 110%;
}
blockquote {
font-style: italic;
}
</style>
<style type="text/css" media="print">
#forkmeongithub {
display: none;
}
#praise {
display: none;
}
</style>
<script type="text/javascript" src="mootools-core-1.3.1-full-compat-yc.js"></script>
<script type="text/javascript" src="TOC.js"></script>
<script type="text/javascript">
window.addEvent('domready', function() {
var toc = new TOC('table-of-contents');
});
</script>
<script type="text/javascript" src="prettify/prettify.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-2127388-6']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script type="text/javascript">
/* <![CDATA[ */
(function() {
var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];
s.type = 'text/javascript';
s.async = true;
s.src = 'http://api.flattr.com/js/0.6/load.js?mode=auto';
t.parentNode.insertBefore(s, t);
})();
/* ]]> */
</script>
</head>
<body onload="prettyPrint();">
<div id="forkmeongithub">
<a href="https://github.com/ManuelKiessling/NodeBeginnerBook"><img style="z-index: 5; position: absolute; top: 0; right: 0; border: 0;" src="https://d3nwyuy0nl342s.cloudfront.net/img/71eeaab9d563c2b3c590319b398dd35683265e85/687474703a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67" alt="Fork me on GitHub" /></a>
</div>
<div id="translations">
<a href="index-es.html">
<div class="flag"><img src="es-flag.png" width="24" height="24" alt="spanish flag" /></div>
<div class="text">Lee este tutorial en Español</div>
</a>
<a href="http://www.nodebeginner.ru">
<div class="flag"><img src="ru-flag.png" width="24" height="24" alt="russian flag" /></div>
<div class="text">Читать этот учебник на русском</div>
</a>
</div>
<div id="buy-the-ebook">
<div class="cover">
<p>
The perfect introduction plus the perfect reference in one bundle!
</p>
<a href="/buy-bundle/"><img src="the_node_beginner_book_cover.png" height="86" width="57" /></a>
<a href="/buy-bundle/"><img src="hands-on_node.js_cover.png" height="86" width="57" /></a>
</div>
<div class="description">
<p>
LeanBundle currently offers
the final version of
<br />
<strong>The Node Beginner Book</strong>
<br />
plus Pedro Teixeira's excellent
<br />
<strong>Hands-on Node.js</strong> for only
<br />
<br />
<strong class="price dollarsign">$</strong><strong class="price">7.99</strong>
<br />
(regular price <del>$10.98</del>)
</p>
</div>
<div class="buy">
<p>
226 pages in total
<br />
PDF, ePub & MOBI
<br />
Direct download
<br />
Free updates
</p>
<a class="buttonlink" href="/buy-bundle/">
<div class="button">Buy this<br />bundle now</div>
</a>
</div>
</div>
<div id="book">
<h1>The Node Beginner Book</h1>
<div id="author">A Node.js tutorial by <a href="http://twitter.com/manuelkiessling">Manuel Kiessling</a></div>
<h2>About</h2>
<p>
The aim of this document is to get you started with developing
applications for Node.js, teaching you everything you need to
know about "advanced" JavaScript along the way. It goes way
beyond your typical "Hello World" tutorial.
</p>
<h3>Status</h3>
<p>
You are reading the final version of this book, i.e., updates
are only done to correct errors or to reflect changes in new
versions of Node.js.
</p>
<p>
The code samples in this book are tested to work with Node.js
version 0.4.9.
</p>
<h3>Intended audience</h3>
<p>
This document will probably fit best for readers that have a
background similar to my own: experienced with at least one
object-oriented language like Ruby, Python, PHP or Java, only little
experience with JavaScript, and completely new to Node.js.
</p>
<p>
Aiming at developers that already have experience with other
programming languages means that this document won't cover
really basic stuff like data types, variables, control structures
and the likes. You already need to know about these to understand
this document.
</p>
<p>
However, because functions and objects in JavaScript are different
from their counterparts in most other languages, these will be
explained in more detail.
</p>
<h3>Structure of this document</h3>
<p>
Upon finishing this document, you will have created a complete web
application which allows the users of this application to view web
pages and upload files.
</p>
<p>
Which, of course, is not exactly
world-changing, but we will go some extra miles and not only create
the code that is "just enough" to make these use cases possible,
but create a simple, yet complete framework to cleanly separate the
different aspects of our application. You will see what I mean in a
minute.
</p>
<p>
We will start with looking at how JavaScript development in Node.js
is different from JavaScript development in a browser.
</p>
<p>
Next, we will stay with the good old tradition of writing a "Hello
World" application, which is a most basic Node.js application that
"does" something.
</p>
<p>
Then, we will discuss what kind of "real" application we want to
build, dissect the different parts which need to be implemented to
assemble this application, and start working on each of these parts
step-by-step.
</p>
<p>
As promised, along the way we will learn about some of the more
advanced concepts of JavaScript, how to make use of them, and
look at why it makes sense to use these concepts instead of
those we know from other programming languages.
</p>
<p>
The source code of the finished application is available through
<a href="https://github.com/ManuelKiessling/NodeBeginnerBook/tree/master/code/application">the NodeBeginnerBook Github repository</a>.
</p>
<div id="table-of-contents-headline">Table of contents</div>
<div id="table-of-contents"></div>
<h2>JavaScript and Node.js</h2>
<h3>JavaScript and You</h3>
<p>
Before we talk about all the technical stuff, let's take a
moment and talk about you and your relationship with
JavaScript. This chapter is here to allow you to estimate
if reading this document any further makes sense for you.
</p>
<p>
If you are like me, you started with HTML "development"
long ago, by writing HTML documents. You came along this
funny thing called JavaScript, but you only used it in a
very basic way, adding interactivity to your web pages
every now and then.
</p>
<p>
What you really wanted was "the real thing", you wanted to
know how to build complex web sites - you learned a
programming language like PHP, Ruby, Java, and started
writing "backend" code.
</p>
<p>
Nevertheless, you kept an eye on JavaScript, you saw that
with the introduction of jQuery, Prototype and the likes,
things got more advanced in JavaScript land, and that this
language really was about more than <em>window.open()</em>.
</p>
<p>
However, this was all still frontend stuff, and although it
was nice to have jQuery at your disposal whenever you felt
like spicing up a web page, at the end of the day you were,
at best, a JavaScript <em>user</em>, but not a JavaScript
<em>developer</em>.
</p>
<p>
And then came Node.js. JavaScript on the server, how cool
is that?
</p>
<p>
You decided that it's about time to check out the old, new
JavaScript. But wait, writing Node.js applications is the
one thing; understanding why they need to be written the
way they are written means - understanding JavaScript.
And this time for real.
</p>
<p>
Here is the problem: Because JavaScript really lives two,
maybe even three lives (the funny little DHMTL helper from
the mid-90's, the more serious frontend stuff like jQuery
and the likes, and now server-side), it's not that easy to
find information that helps you to learn JavaScript the
"right" way, in order to write Node.js applications in a
fashion that makes you feel you are not just using
JavaScript, you are actually developing it.
</p>
<p>
Because that's the catch: you already are an experienced
developer, you don't want to learn a new technique by just
hacking around and mis-using it; you want to be sure that
you are approaching it from the right angle.
</p>
<p>
There is, of course, excellent documentation out there.
But documentation alone sometimes isn't enough. What is
needed is guidance.
</p>
<p>
My goal is to provide a guide for you.
</p>
<h3>A word of warning</h3>
<p>
There are some really excellent JavaScript people out
there. I'm not one of them.
</p>
<p>
I'm really just the guy I talked about in the previous
paragraph. I know a thing or two about developing backend
web applications, but I'm still new to "real" JavaScript
and still new to Node.js. I learned some of the more
advanced aspects of JavaScript just recently.
I'm not experienced.
</p>
<p>
Which is why this is no "from novice to expert" book. It's
more like "from novice to advanced novice".
</p>
<p>
If I don't fail, then this will be the kind of
document I wish I had when starting with Node.js.
</p>
<h3>Server-side JavaScript</h3>
<p>
The first incarnations of JavaScript lived in browsers.
But this is just the context. It defines what you can
do with the language, but it doesn't say much about what
the language itself can do. JavaScript is a "complete"
language: you can use it in many contexts and achieve
everything with it you can achieve with any other
"complete" language.
</p>
<p>
Node.js really is just another context: it allows you to run
JavaScript code in the backend, outside a browser.
</p>
<p>
In order to execute the JavaScript you intend to run in the
backend, it needs to be interpreted and, well, executed.
This is what Node.js does, by making use of Google's V8 VM, the
same runtime environment for JavaScript that Google
Chrome uses.
</p>
<p>
Plus, Node.js ships with a lot of useful modules, so you don't
have to write everything from scratch, like for example
something that outputs a string on the console.
</p>
<p>
Thus, Node.js is really two things: a runtime environment and a
library.
</p>
<p>
In order to make use of these, you need to install Node.js.
Instead of repeating the process here, I kindly ask you to
visit
<a href="https://github.com/joyent/node/wiki/Installation"
title="Building and Installing Node.js">the official
installation instructions</a>. Please come back once you
are up and running.
</p>
<h3>"Hello World"</h3>
<p>
Ok, let's just jump in the cold water and write our first
Node.js application: "Hello World".
</p>
<p>
Open your favorite editor and create a file called
<em>helloworld.js</em>. We want it to write "Hello World"
to STDOUT, and here is the code needed to do that:
</p>
<pre class="prettyprint lang-js">console.log("Hello World");
</pre>
<p>
Save the file, and execute it through Node.js:
</p>
<pre>node helloworld.js</pre>
<p>
This should output <em>Hello World</em> on your terminal.
</p>
<p>
Ok, this stuff is boring, right? Let's write some real
stuff.
</p>
<h2>A full blown web application with Node.js</h2>
<h3>The use cases</h3>
<p>
Let's keep it simple, but realistic:
</p>
<ul>
<li>
The user should be able to use our application with
a web browser
</li>
<li>
The user should see a welcome page when
requesting http://<em>domain</em>/start which displays a
file upload form
</li>
<li>
By choosing an image file to upload and submitting the
form, this image should then be uploaded to
http://<em>domain</em>/upload, where it is displayed once
the upload is finished
</li>
</ul>
<p>
Fair enough. Now, you could achieve this goal by googling
and hacking together <em>something</em>. But that's not
what we want to do here.
</p>
<p>
Furthermore, we don't want to write only the most basic
code to achieve the goal, however elegant and correct this code
might be. We will intentionally add more abstraction than
necessary in order to get a feeling for building more
complex Node.js applications.
</p>
<h3>The application stack</h3>
<p>
Let's dissect our application. Which parts need to be
implemented in order to fulfill the use cases?
</p>
<ul>
<li>
We want to serve web pages, therefore we need an
<strong>HTTP server</strong>
</li>
<li>
Our server will need to answer differently to
requests, depending on which URL the request was
asking for, thus we need some kind of
<strong>router</strong> in order to map requests
to request handlers
</li>
<li>
To fullfill the requests that arrived at the server
and have been routed using the router, we need
actual <strong>request handlers</strong>
</li>
<li>
The router probably should also treat any incoming
POST data and give it to the request handlers in
a convenient form, thus we need <strong>request
data handling</strong>
</li>
<li>
We not only want to handle requests for URLs, we
also want to display content when these URLs are
requested, which means we need some kind of
<strong>view logic</strong> the request handlers
can use in order to send content to the user's
browser
</li>
<li>
Last but not least, the user will be able to upload
images, so we are going to need some kind of
<strong>upload handling</strong> which takes care of
the details
</li>
</ul>
<p>
Let's think a moment about how we would build this stack
with PHP. It's not exactly a secret that the typical setup
would be an Apache HTTP server with mod_php5 installed.
<br/>
Which in turn means that the whole "we need to be able to
serve web pages and receive HTTP requests" stuff doesn't
happen within PHP itself.
</p>
<p>
Well, with node, things are a bit different. Because with
Node.js, we not only implement our application, we also
implement the whole HTTP server. In fact, our web
application and its web server are basically the same.
</p>
<p>
This might sound like a lot of work, but we will see in a
moment that with Node.js, it's not.
</p>
<p>
Let's just start at the beginning and implement the first
part of our stack, the HTTP server.
</p>
<h2>Building the application stack</h2>
<h3>A basic HTTP server</h3>
<p>
When I arrived at the point where I wanted to start with my
first "real" Node.js application, I wondered not only how to
actually code it, but also how to organize my code.
<br/>
Do I need to have everything in one file? Most tutorials on
the web that teach you how to write a basic HTTP server in
Node.js have all the logic in one place. What if I want to
make sure that my code stays readable the more stuff I
implement?
</p>
<p>
Turns out, it's relatively easy to keep the different
concerns of your code separated, by putting them in
modules.
</p>
<p>
This allows you to have a clean main file, which you
execute with Node.js, and clean modules that can be used by
the main file and among each other.
</p>
<p>
So, let's create a main file which we use to start our
application, and a module file where our HTTP server code
lives.
</p>
<p>
My impression is that it's more or less a standard to name
your main file <em>index.js</em>. It makes sense to put our
server module into a file named <em>server.js</em>.
</p>
<p>
Let's start with the server module. Create the file
<em>server.js</em> in the root directory of your project,
and fill it with the following code:
</p>
<pre class="prettyprint lang-js">var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
</pre>
<p>
That's it! You just wrote a working HTTP server. Let's
prove it by running and testing it. First, execute your
script with Node.js:
</p>
<pre>node server.js</pre>
<p>
Now, open your browser and point it at
<a href="http://localhost:8888/" rel="nofollow">http://localhost:8888/</a>.
This should display a web page that says "Hello World".
</p>
<p>
That's quite interesting, isn't it. How about talking about
what's going on here and leaving the question of how to
organize our project for later? I promise we'll get back to
it.
</p>
<h3>Analyzing our HTTP server</h3>
<p>
Well, then, let's analyze what's actually going on here.
</p>
<p>
The first line <em>require</em>s the <em>http</em> module
that ships with Node.js and makes it accessible through the
variable <em>http</em>.
</p>
<p>
We then call one of the functions the http module offers:
<em>createServer</em>. This function returns an object, and
this object has a method named <em>listen</em>, and takes
a numeric value which indicates the port number our HTTP
server is going to listen on.
</p>
<p>
Please ignore for a second the function definition that
follows the opening bracket of <em>http.createServer</em>.
</p>
<p>
We could have written the code that starts our server and
makes it listen at port 8888 like this:
</p>
<pre class="prettyprint lang-js">var http = require("http");
var server = http.createServer();
server.listen(8888);</pre>
<p>
That would start an HTTP server listening at port 8888
and doing nothing else (not even answering any incoming
requests).
</p>
<p>
The really interesting (and, if your background is a more
conservative language like PHP, odd looking) part is the
function definition right there where you would expect the
first parameter of the <em>createServer()</em> call.
</p>
<p>
Turns out, this function definition IS the first (and only)
parameter we are giving to the <em>createServer()</em>
call. Because in JavaScript, functions can be passed around
like any other value.
</p>
<h3>Passing functions around</h3>
<p>
You can, for example, do something like this:
</p>
<pre class="prettyprint lang-js">function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
}
execute(say, "Hello");</pre>
<p>
Read this carefully! What we are doing here is, we pass the
function <em>say</em> as the first parameter to the
<em>execute</em> function. Not the return value of
<em>say</em>, but <em>say</em> itself!
</p>
<p>
Thus, <em>say</em> becomes the local variable
<em>someFunction</em> within <em>execute</em>, and execute
can call the function in this variable by issuing
<em>someFunction()</em> (adding brackets).
</p>
<p>
Of course, because <em>say</em> takes one parameter,
<em>execute</em> can pass such a parameter when calling
<em>someFunction</em>.
</p>
<p>
We can, as we just did, pass a function as a parameter to
another function by its name. But we don't have to take this
indirection of first defining, then passing it - we can
define and pass a function as a parameter to another
function in-place:
</p>
<pre class="prettyprint lang-js">function execute(someFunction, value) {
someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");
</pre>
<p>
We define the function we want to pass to <em>execute</em>
right there at the place where <em>execute</em> expects its
first parameter.
</p>
<p>
This way, we don't even need to give the function a name,
which is why this is called an <em>anonymous function</em>.
</p>
<p>
This is a first glimpse at what I like to call "advanced"
JavaScript, but let's take it step by step. For now, let's
just accept that in JavaScript, we can pass a function as
a parameter when calling another function. We can do this
by assigning our function to a variable, which we then
pass, or by defining the function to pass in-place.
</p>
<h3>How function passing makes our HTTP server work</h3>
<p>
With this knowledge, let's get back to our minimalistic
HTTP server:
</p>
<pre class="prettyprint lang-js">var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
</pre>
<p>
By now it should be clear what we are actually doing here:
we pass the <em>createServer</em> function an anonymous
function.
</p>
<p>
We could achieve the same by refactoring our code to:
</p>
<pre class="prettyprint lang-js">var http = require("http");
function onRequest(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
</pre>
<p>
Maybe now is a good moment to ask: Why are we doing it
that way?
</p>
<h3>Event-driven callbacks</h3>
<p>
The answer is a) not that easy to give (at least for me), and
b) lies in the very nature of how Node.js works. It's
event-driven, which is the reason why it's so fast.
</p>
<p>
You might want to take the time to read Felix
Geisend&ouml;rfer's excellent post
<a href="http://debuggable.com/posts/understanding-node-js:4bd98440-45e4-4a9a-8ef7-0f7ecbdd56cb">Understanding node.js</a>
for some background explanation.
</p>
<p>
It all boils down to the fact that Node.js works event-driven.
Oh and yes, I, too, don't know exactly what that means.
But I will try and explain, why this makes sense for us
who want to write web based applications in Node.js.
</p>
<p>
When we call the <em>http.createServer</em> method, we
of course not only want to have a server listening at
some port, we also want to do something when there is an
HTTP request to this server.
</p>
<p>
The problem is, this happens asynchronously: it happens
at any given time, but we only have a single process in
which our server runs.
</p>
<p>
When writing PHP applications, we aren't bothered by this
at all: whenever there is an incoming HTTP request, the
webserver (usually Apache) forks a new process for just
this request, and starts the according PHP script from
scratch, which is then executed from top to bottom.
</p>
<p>
So in regards of control flow, we are in the midst of our
Node.js program when a new request arrives at port 8888 - how
to handle this without going insane?
</p>
<p>
Well, this is where the event-driven design of
Node.js/JavaScript actually helps, although we need to learn
some new concepts in order to master it. Let's see how
these concepts are applied in our server code.
</p>
<p>
We create the server, and pass a function to the method
creating it. Whenever our server receives a request, the
function we passed will be called.
</p>
<p>
We don't know when this is going to happen, but we now have
a place where we can handle an incoming request. It's our
passed function, no matter if we first defined it or passed
it anonymously.
</p>
<p>
This concept is called a <em>callback</em>. We pass into
some method a function, and the method uses this function
to <em>call back</em> if an event related to the method
occurs.
</p>
<p>
At least for me, this took some time to understand. Just
read Felix' blog post again if you are still unsure.
</p>
<p>
Let's play around a bit with this new concept. Can we prove
that our code continues after creating the server, even if
no HTTP request happened and the callback function we
passed isn't called? Let's try it:
</p>
<pre class="prettyprint lang-js">var http = require("http");
function onRequest(request, response) {
console.log("Request received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
</pre>
<p>
Note that I use <em>console.log</em> to output a text whenever
the <em>onRequest</em> function (our callback) is triggered,
and another text right <em>after</em> starting the HTTP server.
</p>
<p>
When we start this (<em>node server.js</em>, as always), it
will immediately output "Server has started." on the
command line. Whenever we request our server (by opening
<a href="http://localhost:8888/" rel="nofollow">http://localhost:8888/</a>
in our browser), the message "Request received." is printed
on the command line.
</p>
<p>
Event-driven asynchronous server-side JavaScript with
callbacks in action :-)
</p>
<p>
(Note that our server will probably write "Request received."
to STDOUT two times upon opening the page in a browser. That's
because most browser will try to load the favicon by requesting
http://localhost:8888/favicon.ico whenever you open
http://localhost:8888/).
</p>
<h3>How our server handles requests</h3>
<p>
Ok, let's quickly analyze the rest of our server code, that
is, the body of our callback function <em>onRequest()</em>.
</p>
<p>
When the callback fires and our <em>onRequest()</em> function
gets triggered, two parameters are passed into it:
<em>request</em> and <em>response</em>.
</p>
<p>
Those are objects, and you can use their methods to handle
the details of the HTTP request that occured and to respond
to the request (i.e., to actually send something over
the wire back to the browser that requested your server).
</p>
<p>
And our code does just that: Whenever a request is
received, it uses the <em>response.writeHead()</em>
function to send an HTTP status 200 and content-type in the
HTTP response header, and the <em>response.write()</em>
function to send the text "Hello World" in the HTTP
response body.
</p>
<p>
At last, we call <em>response.end()</em> to actually finish
our response.
</p>
<p>
At this point, we don't care for the details of the
request, which is why we don't use the <em>request</em>
object at all.
</p>
<h3>Finding a place for our server module</h3>
<p>
Ok, I promised we will get back to how to organize our
application. We have the code for a very basic HTTP server in
the file <em>server.js</em>, and I mentioned that it's common
to have a main file called <em>index.js</em> which is used
to bootstrap and start our application by making use of the
other modules of the application (like the HTTP server module
that lives in <em>server.js</em>).
</p>
<p>
Let's talk about how to make server.js a real Node.js module
that can be used by our yet-to-be-written <em>index.js</em>
main file.
</p>
<p>
As you may have noticed, we already used modules in our code,
like this:
</p>
<pre class="prettyprint lang-js">var http = require("http");
...
http.createServer(...);
</pre>
<p>
Somewhere within Node.js lives a module called "http", and we can
make use of it in our own code by requiring it and assigning
the result of the require to a local variable.
</p>
<p>
This makes our local variable an object that carries all the
public methods the <em>http</em> module provides.
</p>
<p>
It's common practice to choose the name of the module for the
name of the local variable, but we are free to choose whatever
we like:
</p>
<pre class="prettyprint lang-js">var foo = require("http");
...
foo.createServer(...);
</pre>
<p>
Fine, it's clear how to make use of internal Node.js modules. How
do we create our own modules, and how do we use them?
</p>
<p>
Let's find out by turning our <em>server.js</em> script into a
real module.
</p>
<p>
Turns out, we don't have to change that much. Making some code
a module means we need to <em>export</em> those parts of its
functionality that we want to provide to scripts that require
our module.
</p>
<p>
For now, the functionality our HTTP server needs to export is
simple: scripts requiring our server module simply need to
start the server.
</p>
<p>
To make this possible, we will put our server code into a
function named <em>start</em>, and we will export this
function:
</p>
<pre class="prettyprint lang-js">var http = require("http");
function start() {
function onRequest(request, response) {
console.log("Request received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
</pre>
<p>
This way, we can now create our main file <em>index.js</em>,
and start our HTTP there, although the code for the server is
still in our <em>server.js</em> file.
</p>
<p>
Create a file <em>index.js</em> with the following content:
</p>
<pre class="prettyprint lang-js">var server = require("./server");
server.start();
</pre>
<p>
As you can see, we can use our server module just like any
internal module: by requiring its file and assigning it to
a variable, its exported functions become available to us.
</p>
<p>
That's it. We can now start our app via our main script, and it
still does exactly the same:
</p>
<pre>node index.js</pre>
<p>
Great, we now can put the different parts of our application
into different files and wire them together by making them
modules.
</p>
<p>
We still have only the very first part of our application in
place: we can receive HTTP requests. But we need to do
something with them - depending on which URL the browser
requested from our server, we need to react differently.
</p>
<p>
For a very simple application, you could do this directly
within the callback function <em>onRequest()</em>. But as I said,
let's add a bit more abstraction in order to make our example
application a bit more interesting.
</p>
<p>
Making different HTTP requests point at different parts of our
code is called "routing" - well, then, let's create a module
called <em>router</em>.
</p>
<h3>What's needed to "route" requests?</h3>
<p>
We need to be able to feed the requested URL and possible
additional GET and POST parameters into our router, and based
on these the router then needs to be able to decide which code
to execute (this "code to execute" is the third part of our
application: a collection of request handlers that do the
actual work when a request is received).
</p>
<p>
So, we need to look into the HTTP requests and extract the
requested URL as well as the GET/POST parameters from them.
It could be argued if that should be part of the router or
part of the server (or even a module of its own), but let's
just agree on making it part of our HTTP server for now.
</p>
<p>
All the information we need is available through the
<em>request</em> object which is passed as the first parameter
to our callback function <em>onRequest()</em>. But to interpret
this information, we need some additional Node.js modules, namely
<em>url</em> and <em>querystring</em>.
</p>
<p>
The <em>url</em> module provides methods which allow us to
extract the different parts of a URL (like e.g. the requested
path and query string), and <em>querystring</em> can in turn be
used to parse the query string for request parameters:
</p>
<pre> url.parse(string).query
|
url.parse(string).pathname |
| |
| |
------ -------------------
http://localhost:8888/start?foo=bar&amp;hello=world
--- -----
| |
| |
querystring(string)["foo"] |
|
querystring(string)["hello"]
</pre>
<p>
We can, of course, also use <em>querystring</em> to parse the
body of a POST request for parameters, as we will see
later.
</p>
<p>
Let's now add to our <em>onRequest()</em> function the logic
needed to find out which URL path the browser requested:
</p>
<pre class="prettyprint lang-js">var http = require("http");
var url = require("url");
function start() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
</pre>
<p>
Fine. Our application can now distinguish requests based on the
URL path requested - this allows us to map requests to our
request handlers based on the URL path using our (yet to be
written) router.
</p>
<p>
In the context of our application, it simply means that we will
be able to have requests for the <em>/start</em> and
<em>/upload</em> URLs handled by different parts of our
code. We will see how everything fits together soon.
</p>
<p>
Ok, it's time to actually write our router. Create a new file
called <em>router.js</em>, with the following content:
</p>
<pre class="prettyprint lang-js">function route(pathname) {
console.log("About to route a request for " + pathname);
}
exports.route = route;
</pre>
<p>
Of course, this code basically does nothing, but that's ok for
now. Let's first see how to wire together this router with our
server before putting more logic into the router.
</p>
<p>
Our HTTP server needs to know about and make use of our router.
We could hard-wire this dependency into the server, but because
we learned the hard way from our experience with other
programming languages, we are going to loosely couple server
and router by injecting this dependency (you may want to read
<a href="http://martinfowler.com/articles/injection.html">Martin Fowlers excellent post on Dependency Injection</a>
for background information).
</p>
<p>
Let's first extend our server's <em>start()</em> function in
order to enable us to pass the route function to be used by
parameter:
</p>
<pre class="prettyprint lang-js">var http = require("http");
var url = require("url");
function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(pathname);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
</pre>
<p>
And let's extend our <em>index.js</em> accordingly, that is,
injecting the route function of our router into the server:
</p>
<pre class="prettyprint lang-js">var server = require("./server");
var router = require("./router");
server.start(router.route);
</pre>
<p>
Again, we are passing a function, which by now isn't any news
for us.
</p>
<p>
If we start our application now (<em>node index.js,
as always</em>), and request an URL, you can now see from the
application's output that our HTTP server makes use of our
router and passes it the requested pathname:
</p>
<pre>bash$ node index.js
Request for /foo received.
About to route a request for /foo
</pre>
<p>
(I omitted the rather annoying output for the /favicon.ico
request).
</p>
<h3>Execution in the kingdom of verbs</h3>
<p>
May I once again stray away for a while and talk about
functional programming again?
</p>
<p>
Passing functions is not only a technical consideration.
With regard to software design, it's almost philosophical.
Just think about it: in our index file, we could have passed
the <em>router</em> object into the server, and the server
could have called this object's <em>route</em> function.
</p>
<p>
This way, we would have passed a <em>thing</em>, and the server
would have used this thing to <em>do</em> something. Hey,
router thing, could you please route this for me?
</p>
<p>
But the server doesn't need the thing. It only needs to get
something <em>done</em>, and to get something done, you don't
need things at all, you need <em>actions</em>. You don't need
<em>nouns</em>, you need <em>verbs</em>.
</p>
<p>
Understanding the fundamental mind-shift that's at the core of
this idea is what made me really understand functional
programming.
</p>
<p>
And I did understand it when reading Steve Yegge's masterpiece
<a href="http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html">Execution in the Kingdom of Nouns</a>.
Go read it now, really. It's one of the best writings related
to software I ever had the pleasure to encounter.
</p>
<h3>Routing to real request handlers</h3>
<p>
Back to business. Our HTTP server and our request router are
now best friends and talk to each other as we intended.
</p>
<p>
Of course, that's not enough. "Routing" means, we want to
handle requests to different URLs differently. We would like to
have the "business logic" for requests to <em>/start</em>
handled in another function than requests to <em>/upload</em>.
</p>
<p>
Right now, the routing "ends" in the router, and the router is
not the place to actually "do" something with the requests,
because that wouldn't scale well once our application becomes
more complex.
</p>
<p>
Let's call these functions, where requests are routed to,
<em>request handlers</em>. And let's tackle those next, because
unless we have these in place there isn't much sense in doing
anything with the router for now.
</p>
<p>
New application part, new module - no surprise here. Let's
create a module called requestHandlers, add a placeholder
function for every request handler, and export these as
methods of the module:
</p>
<pre class="prettyprint lang-js">function start() {
console.log("Request handler 'start' was called.");
}
function upload() {
console.log("Request handler 'upload' was called.");
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
This allows us to wire the request handlers into the router,
giving our router something to route to.
</p>
<p>
At this point we need to make a decision: do we hard-code usage
of the requestHandlers module into the router, or do we want a
bit more dependency injection? Although dependency injection,
like every other pattern, shouldn't be used only for the sake
of using it, in this case it makes sense to loosely couple the
router and its request handlers, and thus making the router
really reusable.
</p>
<p>
This means we need to pass the request handlers from our server
into our router, but this feels even more wrong, which is why
we should go the whole way and pass them to the server from our
main file, and passing it on to the router from there.
</p>
<p>
How are we going to pass them? Right now we have two handlers,
but in a real application, this number is going to increase and
vary, and we sure don't want to fiddle around mapping requests
to handlers in the router anytime a new URL / request handler
is added. And having some
<em>if request == x then call handler y</em> in the router
would be more than ugly.
</p>
<p>
A varying number of items, each mapped to a string (the
requested URL)? Well, sounds like an associative array would be
a perfect fit.
</p>
<p>
Well, this finding is slightly disappointed by the fact that
JavaScript doesn't provide associative array - or does it?
Turns out, it's actually objects that we want to use if we need
an associative array!
</p>
<p>
There's a nice introduction to this at
<a href="http://msdn.microsoft.com/en-us/magazine/cc163419.aspx">http://msdn.microsoft.com/en-us/magazine/cc163419.aspx</a>,
let me quote the relevant part:
</p>
<blockquote>
<p>
In C++ or C#, when we’re talking about objects, we're
referring to instances of classes or structs. Objects have
different properties and methods, depending on which
templates (that is, classes) they are instantiated from.
That's not the case with JavaScript objects. In JavaScript,
objects are just collections of name/value pairs - think of a
JavaScript object as a dictionary with string keys.
</p>
</blockquote>
<p>
If JavaScript objects are just collections of name/value pairs,
how can they have methods? Well, the values can be strings,
numbers etc. - or functions!
</p>
<p>
Ok, now finally back to the code. We decided we want to pass
the list of requestHandlers as an object, and in order to
achieve loose coupling we want to inject this object into the
<em>route()</em>.
</p>
<p>
Let's start with putting the object together in our main file
<em>index.js</em>:
</p>
<pre class="prettyprint lang-js">var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
server.start(router.route, handle);
</pre>
<p>
Although <em>handle</em> is more of a "thing" (a collection of
request handlers), I propose we name it like a verb, because
this will result in a fluent expression in our router, as we
will see soon.
</p>
<p>
As you can see, it's really simple to map different URLs to the
same request handler: by adding a key/value pair of
<em>"/"</em> and <em>requestHandlers.start</em>, we can express
in a nice and clean way that not only requests to
<em>/start</em>, but also requests to <em>/</em> shall be
handled by the <em>start</em> handler.
</p>
<p>
After defining our object, we pass it into the server as an
additional parameter. Let's change our <em>server.js</em> to
make use of it:
</p>
<pre class="prettyprint lang-js">var http = require("http");
var url = require("url");
function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(handle, pathname);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
</pre>
<p>
We've added the <em>handle</em> parameter to our
<em>start()</em> function, and pass the handle object on to
the <em>route()</em> callback, as its first parameter.
</p>
<p>
Let's change the <em>route()</em> function accordingly, in our
<em>router.js</em> file:
</p>
<pre class="prettyprint lang-js">function route(handle, pathname) {
console.log("About to route a request for " + pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname]();
} else {
console.log("No request handler found for " + pathname);
}
}
exports.route = route;
</pre>
<p>
What we do here is, we check if a request handler for the given
pathname exists, and if it does, we simply call the according
function. Because we can access our request handler functions
from our object just as we would access an element of an
associative array, we have this nice fluent
<em>handle[pathname]();</em> expression I talked about earlier:
"Please, <em>handle</em> this <em>pathname</em>".
</p>
<p>
Ok, that's all we need to wire server, router, and request
handlers together! When starting our application and requesting
<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>
in our browser, we can prove that the correct request handler
was indeed called:
</p>
<pre>Server has started.
Request for /start received.
About to route a request for /start
Request handler 'start' was called.
</pre>
<p>
And opening <a href="http://localhost:8888/" rel="nofollow">http://localhost:8888/</a>
in our browser proves that these requests, too, are indeed handled by
the <em>start</em> request handler:
</p>
<pre>Request for / received.
About to route a request for /
Request handler 'start' was called.
</pre>
<h3>Making the request handlers respond</h3>
<p>
Beautiful. Now if only the request handlers could actually send
something back to the browser, that would be even better,
right?
</p>
<p>
Remember, the "Hello World" your browser displays upon
requesting a page still comes from the <em>onRequest</em>
function in our <em>server.js</em> file.
</p>
<p>
"Handling request" means "answering requests" after all, thus
we need to enable our request handlers to speak with the
browser just like our <em>onRequest</em> function does.
</p>
<h4>How to not do it</h4>
<p>
The straight-forward approach we - as developers with a
background in PHP or Ruby - might want to follow is actually
very deceitful: it works like a charm, seems to make a lot of
sense, and then suddenly screws things up when we don't expect
it.
</p>
<p>
What I mean by "straight-forward approach" is this: make the
request handlers <em>return()</em> the content they want to
display to the user, and send this response data in the
<em>onRequest</em> function back to the user.
</p>
<p>
Let's just do this, and then see why it's not such an overly
good idea.
</p>
<p>
We start with the request handlers and make them return what we
would like to display in the browser. We need to change
<em>requestHandlers.js</em> to this:
</p>
<pre class="prettyprint lang-js">function start() {
console.log("Request handler 'start' was called.");
return "Hello Start";
}
function upload() {
console.log("Request handler 'upload' was called.");
return "Hello Upload";
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
Good. Likewise, the router needs to return to the server what
the request handlers return to him. We therefore need to edit
<em>router.js</em> like this:
</p>
<pre class="prettyprint lang-js">function route(handle, pathname) {
console.log("About to route a request for " + pathname);
if (typeof handle[pathname] === 'function') {
return handle[pathname]();
} else {
console.log("No request handler found for " + pathname);
return "404 Not found";
}
}
exports.route = route;
</pre>
<p>
As you can see, we also return some text if the request could
not be routed.
</p>
<p>
And last but not least, we need to refactor our server to make
it respond to the browser with the content the request handlers
returned via the router, transforming <em>server.js</em> into:
</p>
<pre class="prettyprint lang-js">var http = require("http");
var url = require("url");
function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
response.writeHead(200, {"Content-Type": "text/plain"});
var content = route(handle, pathname)
response.write(content);
response.end();
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
</pre>
<p>
If we start our rewritten application, everything works like
a charm: requesting <a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>
results in "Hello Start" being displayed in the browser,
requesting <a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>
gives us "Hello Upload", and <a href="http://localhost:8888/foo" rel="nofollow">http://localhost:8888/foo</a>
produces "404 Not found".
</p>
<p>
Ok, then why is that a problem? The short answer: because we
will run into problems if one the request handlers wants to make
use of a non-blocking operation in the future.
</p>
<p>
Let's take a bit more time for the long answer.
</p>
<h4>Blocking and non-blocking</h4>
<p>
As said, the problems will arise when we include non-blocking
operations in the request handlers. But let's talk about
blocking operations first, then about non-blocking operations.
</p>
<p>
And instead of trying to explain what "blocking" and
"non-blocking" means, let's demonstrate ourselves what happens
if we add a blocking operation to our request handlers.
</p>
<p>
To do this, we will modify our <em>start</em> request handler
to make it wait 10 seconds before returning its "Hello Start"
string. Because there is no such thing as <em>sleep()</em> in
JavaScript, we will use a clever hack for that.
</p>
<p>
Please modify <em>requestHandlers.js</em> as follows:
</p>
<pre class="prettyprint lang-js">function start() {
console.log("Request handler 'start' was called.");
function sleep(milliSeconds) {
var startTime = new Date().getTime();
while (new Date().getTime() &lt; startTime + milliSeconds);
}
sleep(10000);
return "Hello Start";
}
function upload() {
console.log("Request handler 'upload' was called.");
return "Hello Upload";
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
Just to make clear what that does: when the function
<em>start()</em> is called, Node.js waits 10 seconds and only then
returns "Hello Start". When calling <em>upload()</em>, it
returns immediately, just like before.
</p>
<p>
(Of course, you should imagine that instead of sleeping for
10 seconds, there would be a real life blocking operation in
<em>start()</em>, like some sort of long-running computation.)
</p>
<p>
Let's see what this change does.
</p>
<p>
As always, we need to restart our server. This time, I ask you
to follow a slightly complex "protocol" in order to see what
happens: First, open two browser windows or tabs. In the first
browser window, please enter <a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>
into the address bar, but do not yet open this url!
</p>
<p>
In the second browser window's address bar, enter <a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>,
and again, please do not yet hit enter.
</p>
<p>
Now, do as follows: hit enter on the first window ("/start"),
then quickly change to the second window ("/upload") and hit
enter, too.
</p>
<p>
What you will notice is this: The /start URL takes 10 seconds
to load, as we would expect. But the /upload URL <em>also</em>
takes 10 seconds to load, although there is no <em>sleep()</em>
in the according request handler!
</p>
<p>
Why? Because <em>start()</em> contains a blocking operation.
Like in "it's blocking everything else from working".
</p>
<p>
And that is a problem, because, as the saying goes: <em>"In
node, everything runs in parallel, except your code"</em>.
</p>
<p>
What that means is that Node.js can handle a lot of concurrent
stuff, but doesn't do this by splitting everything into
threads - in fact, Node.js is single-threaded. Instead, it does
so by running an event loop, and we the developers can make use
of this - we should avoid blocking operations whenever
possible, and use non-blocking operations instead.
</p>
<p>
But to do so, we need to make use of callbacks by passing
functions around to other functions that might do something
that takes some time (like, e.g. sleep for 10 seconds, or query
a database, or do some expensive calculation).
</p>
<p>
This way we are saying <em>"Hey, probablyExpensiveFunction(),
please do your stuff, but I, the single Node.js thread, am not
going to wait right here until you are finished, I will
continue to execute the lines of code below you, so would you
please take this callbackFunction() here and call it when
you are finished doing your expensive stuff? Thanks!"</em>
</p>
<p>
(If you would like to read about that in more detail, please have
a look at Mixu's post on <a href="http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/">Understanding the node.js event loop</a>.)
</p>
<p>
And we will now see why the way we constructed the "request
handler response handling" in our application doesn't allow us
to make proper use of non-blocking operations.
</p>
<p>
Once again, let's try to experience the problem first-hand by
modifying our application.
</p>
<p>
We are going to use our <em>start</em> request handler for this
again. Please modify it to reflect the following (file
<em>requestHandlers.js</em>):
</p>
<pre class="prettyprint lang-js">var exec = require("child_process").exec;
function start() {
console.log("Request handler 'start' was called.");
var content = "empty";
exec("ls -lah", function (error, stdout, stderr) {
content = stdout;
});
return content;
}
function upload() {
console.log("Request handler 'upload' was called.");
return "Hello Upload";
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
As you can see, we just introduced a new Node.js module,
<em>child_process</em>. We did so because it allows us to make
use of a very simple yet useful non-blocking operation:
<em>exec()</em>.
</p>
<p>
What <em>exec()</em> does is, it executes a shell command from
within Node.js. In this example, we are going to use it to get
a list of all files in the current directory ("ls -lah"),
allowing us to display this list in the browser of a user
requesting the <em>/start</em> URL.
</p>
<p>
What the code does is straightforward: create a new variable
<em>content</em> (with an initial value of "empty"), execute
"ls -lah", fill the variable with the result, and return it.
</p>
<p>
As always, we will start our application, and visit
<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>.
</p>
<p>
Which loads a beautiful web page that displays the string
"empty". What's going wrong here?
</p>
<p>
Well, as you may have already guessed, <em>exec()</em> does its
magic in a non-blocking fashion. That's a good thing, because
this way we can execute very expensive shell operations (like,
e.g., copying huge files around or similar stuff) without
forcing our application into a full stop as the blocking
<em>sleep</em> operation did.
</p>
<p>
(If you would like to prove this, replace "ls -lah" with a
more expensive operation like "find /").
</p>
<p>
But we aren't exactly happy with our elegant non-blocking
operation, when our browser doesn't display its result, right?
</p>
<p>
Well, then, let's fix it. And while we are at it, let's try to
understand why the current architecture doesn't work.
</p>
<p>
The problem is that <em>exec()</em>, in order to work
non-blocking, makes use of a callback function.
</p>
<p>
In our example, it's an anonymous function which is passed as
the second parameter to the <em>exec()</em> function call:
</p>
<pre class="prettyprint lang-js">function (error, stdout, stderr) {
content = stdout;
}
</pre>
<p>
And herein lies the root of our problem: our own code is
executed synchronous, which means that immediately after
calling <em>exec()</em>, Node.js continues to execute
<em>return content;</em>. At this point, <em>content</em> is
still "empty", due to the fact that the callback function
passed to <em>exec()</em> has not yet been called - because
<em>exec()</em> operates asynchronous.
</p>
<p>
Now, "ls -lah" is a very inexpensive and fast operation (unless
there are millions of files in the directory). Which is why the
callback is called relatively expeditious - but it nevertheless
happens asynchronously.
</p>
<p>
Thinking about a more expensive command makes this more
obvious: "find /" takes about 1 minute on my
machine, but if I replace "ls -lah" with "find /" in the
request handler, I still immediately receive an HTTP response
when opening the /start URL - it's clear that <em>exec()</em>
does something in the background, while Node.js itself
continues with the application, and we may assume that the
callback function we passed into <em>exec()</em> will be called
only when the "find /" command has finished running.
</p>
<p>
But how can we achieve our goal, i.e. showing the user a list
of files in the current directory?
</p>
<p>
Well, after learning how to <em>not</em> do it, let's discuss
how to make our request handlers respond to browser requests
the right way.
</p>
<h4>Responding request handlers with non-blocking operations</h4>
<p>
I've just used the phrase "the right way". Dangerous stuff.
Quite often, there is no single "right way".
</p>
<p>
But one possible solution for this is, as often with Node.js,
to pass functions around. Let's examine this.
</p>
<p>
Right now, our application is able to transport the content
(which the request handlers would like to display to the user)
from the request handlers to the HTTP server by returning it
up through the layers of the application (request handler -&gt;
router -&gt; server).
</p>
<p>
Our new approach is as follows: instead of bringing the content
to the server, we will bring the server to the content. To be
more precise, we will inject the <em>response</em> object (from
our server's callback function <em>onRequest()</em>) through
the router into the request handlers. The handlers will then be
able to use this object's functions to respond to requests
themselves.
</p>
<p>
Enough explanation, here is the step by step recipe on how to
change our application.
</p>
<p>
Let's start with our <em>server.js</em>:
</p>
<pre class="prettyprint lang-js">var http = require("http");
var url = require("url");
function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(handle, pathname, response);
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
</pre>
<p>
Instead of expecting a return value from the <em>route()</em>
function, we pass it a third parameter, our <em>response</em>
object. Furthermore, we removed any <em>response</em> method
calls from the <em>onRequest()</em> handler, because we now
expect <em>route</em> to take care of that.
</p>
<p>
Next comes <em>router.js</em>:
</p>
<pre class="prettyprint lang-js">function route(handle, pathname, response) {
console.log("About to route a request for " + pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname](response);
} else {
console.log("No request handler found for " + pathname);
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not found");
response.end();
}
}
exports.route = route;
</pre>
<p>
Same pattern: instead of expecting a return value from our
request handlers, we pass the <em>respond</em> object on.
</p>
<p>
If no request handler can be used, we now take care of
responding with a proper "404" header and body ourselves.
</p>
<p>
And last but not least, we modify <em>requestHandlers.js</em>:
</p>
<pre class="prettyprint lang-js">var exec = require("child_process").exec;
function start(response) {
console.log("Request handler 'start' was called.");
exec("ls -lah", function (error, stdout, stderr) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(stdout);
response.end();
});
}
function upload(response) {
console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello Upload");
response.end();
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
Our handler functions need to accept the response parameter,
and have to make use of them in order to respond to the
request directly.
</p>
<p>
The <em>start</em> handler will respond from within the
anonymous <em>exec()</em> callback, and the <em>upload</em>
handler still simply replies with "Hello Upload", but now
by making use of the <em>response</em> object.
</p>
<p>
If we start our application again (<em>node index.js</em>),
this should work as expected.
</p>
<p>
If you would like to prove that an expensive operation behind
<em>/start</em> will no longer block requests for
<em>/upload</em> from answering immediately, then modify your
<em>requestHandlers.js</em> as follows:
</p>
<pre class="prettyprint lang-js">var exec = require("child_process").exec;
function start(response) {
console.log("Request handler 'start' was called.");
exec("find /",
{ timeout: 10000, maxBuffer: 20000*1024 },
function (error, stdout, stderr) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(stdout);
response.end();
});
}
function upload(response) {
console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello Upload");
response.end();
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
This will make HTTP requests to <a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>
take at least 10 seconds, but requests to <a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>
will be answered immediately, even if /start is still
computing.
</p>
<h3>Serving something useful</h3>
<p>
Until now, what we have done is all fine and dandy, but we
haven't created any value for the customers of our
award-winning website.
</p>
<p>
Our server, router, and request handlers are in place, thus now
we can begin to add content to our site which allows our users
to interact and walk through the use case of choosing a file,
uploading this file, and viewing the uploaded file in the
browser. For the sake of simplicity we will assume that only
image files are going to be uploaded and displayed through the
application.
</p>
<p>
Ok, let's take it step by step, but with most of the techniques
and principles of JavaScript explained by now, let's at the
same time accelerate a bit. This author likes to hear himself
talking way too much anyways.
</p>
<p>
Here, step by step means roughly two steps: We will first look
at how to handle incoming POST requests (but not file uploads),
and in a second step, we will make use of an external Node.js
module for the file upload handling. I've chosen this approach
for two reasons.
</p>
<p>
First, handling basic POST requests is
relatively simple with Node.js, but still teaches us enough to
be worth exercising it.
<br/>
Second, handling file uploads (i.e.,
multipart POST requests) is <em>not</em> simple with Node.js,
and therefore is beyond the scope of this tutorial, but using
an external module is itself a lesson that makes sense to be
included in a beginner's tutorial.
</p>
<h4>Handling POST requests</h4>
<p>
Let's keep this banally simple: We will present a textarea that
can be filled by the user and submitted to the server in a POST
request. Upon receiving and handling this request, we will
display the content of the textarea.
</p>
The HTML for this textarea form needs to be served by our
<em>/start</em> request handler, so let's add it right away, in
file <em>requestHandlers.js</em>:
</p>
<pre class="prettyprint lang-js">function start(response) {
console.log("Request handler 'start' was called.");
var body = '&lt;html&gt;'+
'&lt;head&gt;'+
'&lt;meta http-equiv="Content-Type" content="text/html; '+
'charset=UTF-8" /&gt;'+
'&lt;/head&gt;'+
'&lt;body&gt;'+
'&lt;form action="/upload" method="post"&gt;'+
'&lt;textarea name="text" rows="20" cols="60"&gt;&lt;/textarea&gt;'+
'&lt;input type="submit" value="Submit text" /&gt;'+
'&lt;/form&gt;'+
'&lt;/body&gt;'+
'&lt;/html&gt;';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
function upload(response) {
console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello Upload");
response.end();
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
Now if this isn't going to win the Webby Awards, then I don't
know what could. You should see this very simple form when
requesting
<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>
in your browser. If not, you probably didn't restart the
application.
</p>
<p>
I hear you: having view content right in the request handler is
ugly. However, I decided to not include that extra level of
abstraction (i.e., separating view and controller logic) in
this tutorial, because I think that it doesn't teach us
anything worth knowing in the context of JavaScript or Node.js.
</p>
<p>
Let's rather use the remaining screen space for a more
interesting problem, that is, handling the POST request
that will hit our <em>/upload</em> request handler when the
user submits this form.
</p>
<p>
Now that we are becoming expert novices, we are no longer
surprised by the fact that handling POST data is done in a
non-blocking fashion, by using asynchronous callbacks.
</p>
<p>
Which makes sense, because POST requests can potentially be
very large - nothing stops the user from entering text that is
multiple megabytes in size. Handling the whole bulk of data in
one go would result in a blocking operation.
</p>
<p>
To make the whole process non-blocking, Node.js serves our code
the POST data in small chunks, callbacks that are called upon
certain events. These events are <em>data</em> (an new chunk of
POST data arrives) and <em>end</em> (all chunks have been
received).
</p>
<p>
We need to tell Node.js which functions to call back to when
these events occur. This is done by adding <em>listeners</em>
to the <em>request</em> object that is passed to our
<em>onRequest</em> callback whenever an HTTP request is
received.
</p>
<p>
This basically looks like this:
</p>
<pre class="prettyprint lang-js">request.addListener("data", function(chunk) {
// called when a new chunk of data was received
});
request.addListener("end", function() {
// called when all chunks of data have been received
});
</pre>
<p>
The question arises where to implement this logic. We currently
can access the <em>request</em> object in our server only - we
don't pass it on to the router and the request handlers, like
we did with the <em>response</em> object.
</p>
<p>
In my opinion, it's an HTTP servers job to give the application
all the data from a requests it needs to do its job. Therefore,
I suggest we handle the POST data processing right in the
server and pass the final data on to the router and the request
handlers, which then can decide what to do with it.
</p>
<p>
Thus, the idea is to put the <em>data</em> and <em>end</em>
event callbacks in the server, collecting all POST data chunks
in the <em>data</em> callback, and calling the router upon
receiving the <em>end</em> event, while passing the collected
data chunks on to the router, which in turn passes it on to the
request handlers.
</p>
<p>
Here we go, starting with <em>server.js</em>:
</p>
<pre class="prettyprint lang-js">var http = require("http");
var url = require("url");
function start(route, handle) {
function onRequest(request, response) {
var postData = "";
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
request.setEncoding("utf8");
request.addListener("data", function(postDataChunk) {
postData += postDataChunk;
console.log("Received POST data chunk '"+
postDataChunk + "'.");
});
request.addListener("end", function() {
route(handle, pathname, response, postData);
});
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
</pre>
<p>
We basically did three things here: First, we defined that we
expect the encoding of the received data to be UTF-8, we added
an event listener for the "data" event which step by step
fills our new <em>postData</em> variable whenever a new chunk
of POST data arrives, and we moved the call to our router into
the <em>end</em> event callback to make sure it's only called
when all POST data is gathered. We also pass the POST data into
the router, because we are going to need it in our request
handlers.
</p>
<p>
Adding the console logging on every chunk that is received
probably is a bad idea for production code (megabytes of POST
data, remember?), but makes sense to see what happens.
</p>
<p>
I suggest playing around with this a bit. Put small
amounts of text into the textarea as well as lots of text, and
you will see that for the larger texts, the <em>data</em>
callback is indeed called multiple times.
</p>
<p>
Let's add even more awesome to our app. On the /upload page,
we will display the received content. To make this possible, we
need to pass the <em>postData</em> on to the request handlers,
in <em>router.js</em>:
</p>
<pre class="prettyprint lang-js">function route(handle, pathname, response, postData) {
console.log("About to route a request for " + pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname](response, postData);
} else {
console.log("No request handler found for " + pathname);
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not found");
response.end();
}
}
exports.route = route;
</pre>
<p>
And in <em>requestHandlers.js</em>, we include the data in our
response of the <em>upload</em> request handler:
</p>
<pre class="prettyprint lang-js">function start(response, postData) {
console.log("Request handler 'start' was called.");
var body = '&lt;html&gt;'+
'&lt;head&gt;'+
'&lt;meta http-equiv="Content-Type" content="text/html; '+
'charset=UTF-8" /&gt;'+
'&lt;/head&gt;'+
'&lt;body&gt;'+
'&lt;form action="/upload" method="post"&gt;'+
'&lt;textarea name="text" rows="20" cols="60"&gt;&lt;/textarea&gt;'+
'&lt;input type="submit" value="Submit text" /&gt;'+
'&lt;/form&gt;'+
'&lt;/body&gt;'+
'&lt;/html&gt;';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
function upload(response, postData) {
console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("You've sent: " + postData);
response.end();
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
That's it, we are now able to receive POST data and use it in
our request handlers.
</p>
<p>
One last thing for this topic: what we pass on to the router
and the request handlers is the complete body of our POST
request. We will probably want to consume the individual fields
that make up the POST data, in this case, the value of the
<em>text</em> field.
</p>
<p>
We already read about the <em>querystring</em> module, which
assists us with this:
</p>
<pre class="prettyprint lang-js">var querystring = require("querystring");
function start(response, postData) {
console.log("Request handler 'start' was called.");
var body = '&lt;html&gt;'+
'&lt;head&gt;'+
'&lt;meta http-equiv="Content-Type" content="text/html; '+
'charset=UTF-8" /&gt;'+
'&lt;/head&gt;'+
'&lt;body&gt;'+
'&lt;form action="/upload" method="post"&gt;'+
'&lt;textarea name="text" rows="20" cols="60"&gt;&lt;/textarea&gt;'+
'&lt;input type="submit" value="Submit text" /&gt;'+
'&lt;/form&gt;'+
'&lt;/body&gt;'+
'&lt;/html&gt;';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
function upload(response, postData) {
console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("You've sent the text: "+
querystring.parse(postData).text);
response.end();
}
exports.start = start;
exports.upload = upload;
</pre>
<p>
Well, for a beginner's tutorial, that's all there is to say
about handling POST data.
</p>
<h4>Handling file uploads</h4>
<p>
Let's tackle our final use case. Our plan was to allow users to
upload an image file, and display the uploaded image in the
browser.
</p>
<p>
Back in the 90's this would have qualified as a business model
for an IPO, today it must suffice to teach us two things: how
to install external Node.js libraries, and how to make use of
them in our own code.
</p>
<p>
The external module we are going to use is
<em>node-formidable</em> by Felix Geisend&ouml;rfer. It nicely
abstracts away all the nasty details of parsing incoming file
data. At the end of the day, handling incoming files is "only"
about handling POST data - but the devil really <em>is</em> in
the details here, and using a ready-made solution makes a lot
of sense in this case.
</p>
<p>
In order to make use of Felix' code, the according Node.js
module needs to be installed. Node.js ships with its own
package manager, dubbed <em>NPM</em>. It allows us to install
external Node.js modules in a very convenient fashion. Given a
working Node.js installation, it boils down to issuing
</p>
<pre class="prettyprint lang-bash">npm install formidable
</pre>
<p>
on our command line. If the following output ends with
</p>
<pre class="prettyprint lang-bash">npm info build Success: formidable@1.0.2
npm ok
</pre>
<p>
then we are good to go.
</p>
<p>
The <em>formidable</em> module is now available to our own
code - all we need to do is requiring it just like one of the
built-in modules we used earlier:
</p>
<pre class="prettyprint lang-js">var formidable = require("formidable");
</pre>
<p>
The metaphor formidable uses is that of a form being submitted
via HTTP POST, making it parseable in Node.js. All we need to
do is create a new <em>IncomingForm</em>, which is an
abstraction of this submitted form, and which can then be used
to parse the <em>request</em> object of our HTTP server for the
fields and files that were submitted through this form.
</p>
<p>
The example code from the node-formidable project page shows
how the different parts play together:
</p>
<pre class="prettyprint lang-js">var formidable = require('formidable'),
http = require('http'),
sys = require('sys');
http.createServer(function(req, res) {
if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
// parse a file upload
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
res.end(sys.inspect({fields: fields, files: files}));
});
return;
}
// show a file upload form
res.writeHead(200, {'content-type': 'text/html'});
res.end(
'&lt;form action="/upload" enctype="multipart/form-data" '+
'method="post"&gt;'+
'&lt;input type="text" name="title"&gt;&lt;br&gt;'+
'&lt;input type="file" name="upload" multiple="multiple"&gt;&lt;br&gt;'+
'&lt;input type="submit" value="Upload"&gt;'+
'&lt;/form&gt;'
);
}).listen(8888);
</pre>
<p>
If we put this code into a file and execute it through
<em>node</em>, we are able to submit a simple form, including a
file upload, and see how the <em>files</em> object, which is passed
to the callback defined in the <em>form.parse</em> call, is
structured:
</p>
<pre class="lang-js">received upload:
{ fields: { title: 'Hello World' },
files:
{ upload:
{ size: 1558,
path: '/tmp/1c747974a27a6292743669e91f29350b',
name: 'us-flag.png',
type: 'image/png',
lastModifiedDate: Tue, 21 Jun 2011 07:02:41 GMT,
_writeStream: [Object],
length: [Getter],
filename: [Getter],
mime: [Getter] } } }
</pre>
<p>
In order to make our use case happen, what we need to do is
to include the form-parsing logic of formidable into our code
structure, plus we will need to find out how to serve the
content of the uploaded file (which is saved into the
<em>/tmp</em> folder) to a requesting browser.
</p>
<p>
Let's tackle the latter one first: if there is an image file on
our local hardrive, how do we serve it to a requesting browser?
</p>
<p>
We are obviously going to read the contents of this file into
our Node.js server, and unsurprisingly, there is a module for
that - it's called <em>fs</em>.
</p>
<p>
Let's add another request handler for the URL <em>/show</em>,
which will hardcodingly display the contents of the file
<em>/tmp/test.png</em>. It of course makes a lot of sense to
save a real png image file to this location first.
</p>
<p>
We are going to modify <em>requestHandlers.js</em> as follows:
</p>
<pre class="prettyprint lang-js">var querystring = require("querystring"),
fs = require("fs");
function start(response, postData) {
console.log("Request handler 'start' was called.");
var body = '&lt;html&gt;'+
'&lt;head&gt;'+
'&lt;meta http-equiv="Content-Type" '+
'content="text/html; charset=UTF-8" /&gt;'+
'&lt;/head&gt;'+
'&lt;body&gt;'+
'&lt;form action="/upload" method="post"&gt;'+
'&lt;textarea name="text" rows="20" cols="60"&gt;&lt;/textarea&gt;'+
'&lt;input type="submit" value="Submit text" /&gt;'+
'&lt;/form&gt;'+
'&lt;/body&gt;'+
'&lt;/html&gt;';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
function upload(response, postData) {
console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("You've sent the text: "+
querystring.parse(postData).text);
response.end();
}
function show(response, postData) {
console.log("Request handler 'show' was called.");
fs.readFile("/tmp/test.png", "binary", function(error, file) {
if(error) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
} else {
response.writeHead(200, {"Content-Type": "image/png"});
response.write(file, "binary");
response.end();
}
});
}
exports.start = start;
exports.upload = upload;
exports.show = show;
</pre>
<p>
We also need to map this new request handler to the URL
<em>/show</em> in file <em>index.js</em>:
</p>
<pre class="prettyprint lang-js">var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");
var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
handle["/show"] = requestHandlers.show;
server.start(router.route, handle);
</pre>
<p>
By restarting the server and opening
<a href="http://localhost:8888/show" rel="nofollow">http://localhost:8888/show</a>
in the browser, the image file saved at <em>/tmp/test.png</em>
should be displayed.
</p>
<p>
Fine. All we need to do now is
<ul>
<li>
add a file upload element to the form which is served
at <em>/start</em>,
</li>
<li>
integrate node-formidable into the <em>upload</em>
request handler, in order to save the uploaded file to
<em>/tmp/test.png</em>,
</li>
<li>
embed the uploaded image into the HTML output of the
<em>/upload</em> URL.
</li>
</ul>
</p>
<p>
Step 1 is simple. We need to add an encoding type of
<em>multipart/form-data</em> to our HTML form, remove the
textarea, add a file upload input field, and change the submit
button text to "Upload file". Let's do just that in file
<em>requestHandlers.js</em>:
</p>
<pre class="prettyprint lang-js">var querystring = require("querystring"),
fs = require("fs");
function start(response, postData) {
console.log("Request handler 'start' was called.");
var body = '&lt;html&gt;'+
'&lt;head&gt;'+
'&lt;meta http-equiv="Content-Type" '+
'content="text/html; charset=UTF-8" /&gt;'+
'&lt;/head&gt;'+
'&lt;body&gt;'+
'&lt;form action="/upload" enctype="multipart/form-data" '+
'method="post"&gt;'+
'&lt;input type="file" name="upload"&gt;'+
'&lt;input type="submit" value="Upload file" /&gt;'+
'&lt;/form&gt;'+
'&lt;/body&gt;'+
'&lt;/html&gt;';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
function upload(response, postData) {
console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("You've sent the text: "+
querystring.parse(postData).text);
response.end();
}
function show(response, postData) {
console.log("Request handler 'show' was called.");
fs.readFile("/tmp/test.png", "binary", function(error, file) {
if(error) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
} else {
response.writeHead(200, {"Content-Type": "image/png"});
response.write(file, "binary");
response.end();
}
});
}
exports.start = start;
exports.upload = upload;
exports.show = show;
</pre>
<p>
Great. The next step is a bit more complex of course. The first
problem is: we want to handle the file upload in our
<em>upload</em> request handler, and there, we will need to pass
the <em>request</em> object to the <em>form.parse</em> call of
node-formidable.
</p>
<p>
But all we have is the <em>response</em> object
and the <em>postData</em> array. Sad panda. Looks like we will have
to pass the <em>request</em> object all the way from the server to
the router to the request handler. There may be more elegant
solutions, but this approach should do the job for now.
</p>
<p>
And while we are at it, let's remove the whole <em>postData</em> stuff in
our server and request handlers - we won't need it for handling the
file upload, and it even raises a problem: we already "consumed"
the <em>data</em> events of the <em>request</em> object in the
server, which means that <em>form.parse</em>, which also needs to
consume those events, wouldn't receive any more data from them
(because Node.js doesn't buffer any data).
</p>
<p>
Let's start with <em>server.js</em> - we remove the postData
handling and the <em>request.setEncoding</em> line (which is going
to be handled by node-formidable itself), and we pass
<em>request</em> to the router instead:
</p>
<pre class="prettyprint lang-js">var http = require("http");
var url = require("url");
function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
route(handle, pathname, response, request);
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
exports.start = start;
</pre>
<p>
Next comes <em>router.js</em> - we don't need to pass <em>postData</em>
on anymore, and instead pass <em>request</em>:
</p>
<pre class="prettyprint lang-js">function route(handle, pathname, response, request) {
console.log("About to route a request for " + pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname](response, request);
} else {
console.log("No request handler found for " + pathname);
response.writeHead(404, {"Content-Type": "text/html"});
response.write("404 Not found");
response.end();
}
}
exports.route = route;
</pre>
<p>
Now, the <em>request</em> object can be used in our <em>upload</em>
request handler function. node-formidable will handle the details
of saving the uploaded file to a local file within <em>/tmp</em>,
but we need to make sure that this file is renamed to
<em>/tmp/test.png</em> ourselves. Yes, we keep things really simple
and assume that only PNG images will be uploaded.
</p>
<p>
For now, <em>fs.renameSync(path1, path2)</em> will do the job.
Beware! As the name implies, it works synchronous, thus if the
rename operation should be expensive and take a long time, it will
lead to blocking. Let's just agree that we are all grown-ups here
and know what we are doing.
</p>
<p>
Let's put the pieces of managing the uploaded file and renaming it
together now, in file <em>requestHandlers.js</em>:
</p>
<pre class="prettyprint lang-js">var querystring = require("querystring"),
fs = require("fs"),
formidable = require("formidable");
function start(response) {
console.log("Request handler 'start' was called.");
var body = '&lt;html&gt;'+
'&lt;head&gt;'+
'&lt;meta http-equiv="Content-Type" content="text/html; '+
'charset=UTF-8" /&gt;'+
'&lt;/head&gt;'+
'&lt;body&gt;'+
'&lt;form action="/upload" enctype="multipart/form-data" '+
'method="post"&gt;'+
'&lt;input type="file" name="upload" multiple="multiple"&gt;'+
'&lt;input type="submit" value="Upload file" /&gt;'+
'&lt;/form&gt;'+
'&lt;/body&gt;'+
'&lt;/html&gt;';
response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}
function upload(response, request) {
console.log("Request handler 'upload' was called.");
var form = new formidable.IncomingForm();
console.log("about to parse");
form.parse(request, function(err, fields, files) {
console.log("parsing done");
fs.renameSync(files.upload.path, "/tmp/test.png");
response.writeHead(200, {"Content-Type": "text/html"});
response.write("received image:&lt;br/&gt;");
response.write("&lt;img src='/show' /&gt;");
response.end();
});
}
function show(response) {
console.log("Request handler 'show' was called.");
fs.readFile("/tmp/test.png", "binary", function(error, file) {
if(error) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
} else {
response.writeHead(200, {"Content-Type": "image/png"});
response.write(file, "binary");
response.end();
}
});
}
exports.start = start;
exports.upload = upload;
exports.show = show;
</pre>
<p>
And that's it. Restart the server, and the complete use case will
be available. Select a local PNG image from your hardrive, upload
it to the server, and have it displayed in the web page.
</p>
<h2>Conclusion and outlook</h2>
<p>
Congratulations, our mission is accomplished! We wrote a simple
yet full-fledged Node.js web application. We talked about
server-side JavaScript, functional programming, blocking and
non-blocking operations, callbacks, events, custom, internal
and external modules, and a lot more.
</p>
<p>
Of course, there's a lot of stuff we did not talk about:
how to talk to a database, how to write unit tests, how to
create external modules that are installable via NPM, or even
something simple like how to handle GET requests.
</p>
<p>
But that's the fate of every book aimed at beginners - it can't
talk about every single aspect in every single detail.
</p>
<p>
The good news is, the Node.js community is extremly vibrant
(think of an ADHD kid on caffeine, but in a positive way),
which means there are a lot of resources out there, and a lot
of places to get your questions answered. The
<a href="https://github.com/joyent/node/wiki">Node.js community wiki</a>
and <a href="http://www.nodecloud.org/">the NodeCloud directory</a>
are probably the best starting points for more information.
</p>
</div>
<div id="workinprogressnote">
<p>
This document was last updated on
<strong>Sunday, July 10, 2011 at 3:52 AM EDT</strong>.
</p>
<p>
The best way to stay informed about updates is to follow
<br />
<a href="http://twitter.com/manuelkiessling">@ManuelKiessling on Twitter</a>.
</p>
</div>
<div id="praise">
<div class="praise">
<div class="comment">
"I love nodebeginner.org - concise, direct to the point and
even enjoyable to read."
</div>
<div class="author">Gojko Adzic, author of <em>Specification by Example</em> and <em>Bridging the Communication Gap</em></div>
</div>
<div class="praise">
<div class="comment">
"This is one of the best tutorials I've read.
As a former Java coder, I've always found JavaScript
to be a black art, but you have really simplified
things with this tutorial."
</div>
<div class="author">Erskine, from the comments</div>
</div>
<div class="praise">
<div class="comment">
"This is one of the few beginner articles I made it all the
way through because of how well it's written."
</div>
<div class="author">
Paul Gibler, from the comments
</div>
</div>
<div class="praise">
<div class="comment">
"Indispensable."
</div>
<div class="author">
@lecolibrilibre, on Twitter
</div>
</div>
<div class="praise">
<div class="comment">
"I just wanted to drop you a note to say thank you for
writing such an excellent introduction to node. Your book's
explanation is fantastic, and I can't wait for you to
finish it!"
</div>
<div class="author">
Seth McLaughlin, via eMail
</div>
</div>
</div>
<div id="donate">
<p>
<em>The Node Beginner Book</em> is and will always be available for free.
<br />
Furthermore, no money is needed to keep the project alive and progressing.
<br />
But if you would like to support the author, you can do so using Flattr or PayPal:
</p>
<div id="flattr">
<a class="FlattrButton" style="display:none;" href="http://www.nodebeginner.org"></a>
</div>
<div id="paypal">
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<fieldset>
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="AWJS7PD37SQ5G" />
<input type="image" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/en_US/i/btn/btn_donateCC_LG.gif" name="submit" alt="PayPal - The safer, easier way to pay online!" />
<img alt="" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/de_DE/i/scr/pixel.gif" width="1" height="1" />
</fieldset>
</form>
</div>
<p>
Or, have a look at <a href="/buy/">the eBook version (Kindle, ePub, PDF)</a>,
<br />
which is available for $4.99 via Leanpub.
</p>
</div>
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_shortname = 'nodebeginner';
var disqus_identifier = 'nodebeginner-book';
var disqus_url = 'http://www.nodebeginner.org/';
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<div id="footer">
<p id="ccimage">
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png" /></a>
</p>
<p>
<span xmlns:dct="http://purl.org/dc/terms/">The Node Beginner Book</span>
by
<a xmlns:cc="http://creativecommons.org/ns#" href="http://manuel.kiessling.net" rel="cc:attributionURL">Manuel Kiessling</a>
is licensed under a
<br />
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License</a>.
<br />
Permissions beyond the scope of this license may be available at <a xmlns:cc="http://creativecommons.org/ns#" href="mailto:manuel@kiessling.net" rel="cc:morePermissions">manuel@kiessling.net</a>.
</p>
</div>
</body>
</html>