Node JS in 24 Hours
Node JS in 24 Hours
js Is Used For
Try It Yourself
Here, we are demonstrating input and output:
1. Open a terminal and type the following command:
echo 'I must learn about Node.js'
2. You should see the line echoed back to you.
3. Note that your keyboard is the input here.
4. Note that your monitor is the output here.
Although this appears simple, there is a lot going on here. The
program being used is echo, a simple utility for echoing whatever
text it is given. What is happening in terms of the movement of data
is as follows:
1. The text string is passed to the echo program (input).
2. The text string goes through the echo program’s logic.
3. The echo program outputs it to the terminal (output).
DEALING WITH INPUT
In computer programs, input is often needed from users. This can be
in the form of prompts from command-line scripts, forms, emails, or
text messages. Much of computer programming is writing software
that solves a problem and then deals with all of the possible
unpredictability surrounding the problem. Consider a simple web
form that requests the following information:
• First name
• Last name
• Email
When the user submits the form, the data will be saved to a database
and displayed on a web page. But, many things can go wrong here:
• The user does not enter a first name.
• The user does not enter a last name.
• The user does not enter an email.
• The user enters an email that is not valid.
• The user enters text that is too long for the database field to hold.
• The user does not enter any data.
For developers, mapping these scenarios and defining responses for
them is commonplace. This is important because it leads to stable
software, and developers often choose to write automated tests for
scenarios that they can run against their code base to ensure that
their code is working as expected. The following is an example from
a testing framework called Cucumber that allows developers to write
tests in plain English:
function fetchPage(url) {
var start = new Date();
http.get({ host: url }, function(res) {
console.log("Got response from: " + url);
console.log('Request took:', new Date() - start, 'ms');
});
}
The inputs here are the responses from three different web servers,
and Node.js sends the output to your terminal. If you run the same
code again, you might expect the same results, but you see a
different output:
$('#target').click(function() {
alert('Hey you clicked me!');
});
SUMMARY
In this hour, you were introduced to more about what Node.js is and
the problems that Node.js is designed to solve. You understood the
idea that web applications have moved from serving single HTML
pages to become much more complicated. You were introduced to
the idea of I/O and how inputs and outputs have become more
numerous in modern web applications. You learned that predicating
the behavior of humans in relation to time and sequence in software
development is difficult and saw how JavaScript offers an event-
driven approach to respond to this idea. You then learned about the
idea of concurrency, one of the main problems that Node.js is
attempting to solve.
You were introduced to the idea that concurrency is a long-standing
problem in software development and that Node.js is one response
to that problem, particularly in the context of working with
networks. In this hour, you learned more about what Node.js is
trying to solve. In the next hour, you learn more about the approach
it takes to concurrency.
Q&A
Q. I’m developing small content sites. Is Node.js a good fit?
A. You can absolutely create small content sites with Node.js, and a
number of frameworks have been created to help you do that. Note,
though, that when Node.js was designed and created, it was not
designed with this in mind.
Q. Concurrency seems difficult to understand. Should I worry if I
don’t fully understand it now?
A. Concurrency is a difficult concept, and you will be introduced to
practical examples in future hours. For now, understand that
concurrency means many people trying to do the same thing at once.
Q. Does I/O relate to the .io domain that I see appearing around the
web?
A. Yes. Developers working with real-time and Node.js applications
realized that they were dealing heavily with I/O in their applications.
The .io domain actually has nothing to do with computing; it is the
domain for the Indian Ocean. You might have seen this domain used
in relation to real-time software with sites like http://socket.io.
WORKSHOP
This workshop contains quiz questions to help cement your learning
in this hour.
Quiz
1. What are some of the ways that web applications have changed
since the time when websites were just HTML documents?
2. What criteria do you consider are a good fit for using Node.js?
3. Why is JavaScript an event-driven language?
Quiz Answers
1. Some of the ways that web applications have become more
complex include the addition of scripting languages and databases.
Increasingly data is now distributed around the web and glued
together using APIs and networks.
2. Node.js is a good fit when your application needs to send and
receive data over a network. This might be third-party APIs,
networked devices, or real-time communication between a browser
and server.
3. JavaScript is structured around events that initially were related
to the Document Object Model (DOM). This allows developers to do
things when events happen. Some examples of these events are a
user clicking an element or the page finishing loading. Using events
allows developers to write listeners for events that fire whenever the
event happens.
EXERCISES
1. If you have developed any websites or software, pick one and try
to draw an input and output diagram for it. Understand the devices
or things that represent the inputs and outputs.
2. Pick your favorite computer game and try to understand the
inputs and outputs involved in the game. How many inputs are
there? Are there things that could happen at the same time or nearly
the same time?
3. Take some time to read the following two articles: “Node.js is
good for solving problems I don’t have” (http://bit.ly/LyYFMx) and
“Node.js is not a cancer, you are just a moron”
(http://bit.ly/KB1HcW). Do not worry if you do not understand all
the articles, but understand that the approach that Node.js is taking
to solving concurrency is innovative, controversial, and provokes
healthy debate.
Hour 4. Callbacks
$('p').hide('slow');
Comparing the two code examples, the first one adds an anonymous
function as a second argument and is called once the first function
has finished. In the second example, there is no callback. Because
functions in JavaScript are first-class objects, they can be passed as
arguments to other functions in this way. This allows you to write
code that says, “Do this and when you are finished doing that, do
this.” To illustrate the difference between writing code with and
without callbacks, you look at two jQuery examples in the browser.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Callback Example</title>
</head>
<body>
<h1>Callback Example</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer ut augue
et orcialiquam aliquam. Lorem ipsum dolor sit amet, consectetur
adipiscing elit.
Pellentesquehabitant morbi tristique senectus et netus et malesuada
fames ac turpis
egestas. Etiamfermentum dictum convallis. In at diam et orci
sodales sollicitudin.
Sed viverra, orcisit amet faucibus condimentum, nibh augue
consectetur ipsum, eu
tristique dolor diaminterdum tellus. Donec in diam nunc. Nulla
sollicitudin elit
sit amet neque elementum accursus nibh lobortis.</p>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js"></script>
<script>
$(function () {
$('p').hide('slow');
alert("The paragraph is now hidden");
});
</script>
</body>
</html>
Figure 4.1. An alert being shown before the hide() method has finished
In the previous example, you saw that the alert message was shown
before the paragraph was hidden. This is because the alert is
executed before the paragraph has finished hiding. This is a great
example of where callbacks can be used to ensure that something is
executed after something else has finished. Here is the same
example using a callback:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Callback Example</title>
</head>
<body>
<h1>Callback Example</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Integer ut augue
et orcialiquam aliquam. Lorem ipsum dolor sit amet, consectetur
adipiscing elit.
Pellentesquehabitant morbi tristique senectus et netus et malesuada
fames ac turpis
egestas. Etiamfermentum dictum convallis. In at diam et orci
sodales sollicitudin.
Sed viverra, orcisit amet faucibus condimentum, nibh augue
consectetur ipsum, eu
tristique dolor diaminterdum tellus. Donec in diam nunc. Nulla
sollicitudin elit
sit amet neque elementum accursus nibh lobortis.</p>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js"></script>
<script>
$(function () {
$('p').hide('slow', function() {
alert("The paragraph is now hidden");
});
});
</script>
</body>
</html>
The example now shows the alert after the paragraph has been
hidden, and the alert now appears correctly after the event rather
than before it.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour04/example02.
Follow these steps to use callbacks with jQuery:
1. Create a file called index.html and copy the following code into it:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Callback Example</title>
</head>
<body>
<h1>Callback Example</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer
ut augue et orcialiquam aliquam. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Pellentesquehabitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Etiamfermentum dictum convallis. In at
diam
et orci sodales sollicitudin. Sed viverra, orcisit amet faucibus condimentum,
nibh augue consectetur ipsum, eu tristique dolor diaminterdum tellus.
Donec
in diam nunc. Nulla sollicitudin elit sit amet neque elementum accursus nibh
lobortis.</p>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></s
cript>
<script>
$(function () {
$('p').hide('slow', function() {
alert("The paragraph is now hidden");
});
});
</script>
</body>
</html>
2. Open the file in a web browser.
3. After a short pause, you should see the paragraph is hidden, and after that
an alert is displayed (see Figure 4.2).
Figure 4.2. An alert being shown after the hide() method has finished
THE ANATOMY OF A CALLBACK
You have seen how callbacks work in jQuery, but how does a
callback work internally in JavaScript? The key concept to grasp is
that functions can be passed into other functions as arguments and
then called. In the following example, a haveBreakfast function is
created that takes two arguments of “food” and “drink.” The third
argument is “callback,” and this is expected to be a function. The
function haveBreakfast logs what is being eaten to the console and
then calls the callback function that has been passed in as an
argument.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour04/example03.
The following steps illustrate callbacks:
1. Create a file called app.js and copy the following code into it:
function haveBreakfast(food, drink, callback) {
console.log('Having breakfast of ' + food + ', ' + drink);
if (callback && typeof(callback) === "function") {
callback();
}
}
var fs = require('fs');
var fs = require('fs'),
http = require('http');
function sleep(milliseconds) {
var start = new Date().getTime();
while ((new Date().getTime() - start) < milliseconds){
}
}
function fetchPage() {
console.log('fetching page');
sleep(2000); // simulate time to fetch a web page
console.log('data returned from requesting page');
}
function fetchApi() {
console.log('fetching api');
sleep(2000); // simulate time to fetch from an api
console.log('data returned from the api');
}
fetchPage();
fetchApi();
In this example, the fetchPage() function simulates fetching a page
from the Internet and the fetchApi() function simulates fetching data
from a third-party API. They are not fetching actual data, but
demonstrating what happens in a synchronous style of programming
when the amount of time it takes to respond is unknown.
If you run this example, you see the following output:
fetching page
page fetched
fetching api
data returned from the api
When the script runs, the fetchPage() function is called, and until this
returns, the script’s execution is blocked. Until
the fetchPage() function returns, it cannot move onto
the fetchApi() function. This is known as a blocking operation,
because this style of coding blocks the processing until a function
returns. Effectively, one thing happens after the other.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour04/example05.
The following steps demonstrate synchronous or blocking code:
1. Copy the code in Listing 4.2 into a file on your system called app.js.
2. Run the script:
node app.js
3. You should see the simulation of a page being fetched from the Internet,
and when it is finished, a simulation of fetching data from an API is made.
The point to understand here is that they happen one after the other and
that, until one function has finished, the execution is blocked.
Node.js almost never uses this style of coding. Instead, callbacks are
called asynchronously. Listing 4.3demonstrates the same
operations, but using Node’s asynchronous style. Note that instead
of using a simulated sleep() function, this example makes a request
over the network to a web service that simulates slow responses.
Listing 4.3. Asynchronous (or Non-Blocking) Code
function fetchPage() {
console.log('fetching page');
http.get({ host: 'trafficjamapp.herokuapp.com', path: '/?
delay=2000' },
function(res) {
console.log('data returned from requesting page');
}).on('error', function(e) {
console.log("There was an error" + e);
}
);
}
function fetchApi() {
console.log('fetching api');
http.get({ host: 'trafficjamapp.herokuapp.com', path: '/?
delay=2000' },
function(res) {
console.log('data returned from the api');
}).on('error', function(e) {
console.log("There was an error" + e);
}
);
}
fetchPage();
fetchApi();
When this code is run, instead of waiting for the fetchPage() function
to return, the fetchApi()function is called immediately afterward.
This is possible because the code is non-blocking through the use of
callbacks. Once called, both functions then listen for a response to be
returned by the remote server, and this fires the callback functions.
Note that there is no guarantee as to which order these functions
might return in. This depends on the network. If you run this script,
you see the following output:
fetching page
fetching api
page fetched
data returned from the api
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour04/example06.
The following steps demonstrate asynchronous code:
1. Copy the code in Listing 4.3 into a file on your system called app.js.
2. Run the script:
node app.js
3. You should see the simulation of a database query being made and data
being fetched from an API asynchronously. The point to understand here is
that now an asynchronous style is used so these operations can be performed
at the same time and when the data comes back a callback is fired.
By running these two examples in your terminal, it should become
clear how these two styles of coding affect what is run and when.
Node.js is opinionated on how best to handle concurrency (or doing
more than one thing at once), within the context of networks and
I/O operations, advocating the asynchronous style.
If you are new to Node.js and JavaScript, the terms asynchronous,
synchronous, blocking, and non-blocking may be confusing. These
are used liberally throughout the Node.js community and within
documentation, so it is important to understand them.
The terms synchronous and blocking can be used interchangeably
and refer to the example that you saw where execution of code stops
until a function returns. A script is not able to continue if an
operation blocks, and for the end user, this means that they simply
have to wait.
The terms asynchronous and non-blocking can also be used
interchangeably and refer to the callback-based approach of allowing
a script to perform operations in parallel. A script does not need to
wait for the outcome of an operation before proceeding, because this
will be handled by callback when the event happens. Using the
asynchronous approach, operations no longer have to happen one
after another.
THE EVENT LOOP
At this point, you might be wondering how all of this magic happens.
Node.js uses JavaScript’s event loop to support the asynchronous
programming style that it advocates. This can be another tricky
concept to come to grips with, but it basically allows callback
functions to be saved and then run at a point in the future when an
event happens. This might be data being returned from a database
or an HTTP request returning data. Because the execution of the
callback function is deferred until the event happens, there is no
need to halt the execution, and control can be returned to the Node
runtime environment so that other things can happen.
The event loop is not specific to JavaScript, but the language excels
at it. JavaScript was designed around the event loop as a way to
respond to different events happening in the browser as someone
interacts with a web page. This includes events like clicking a mouse
or rolling over a part of the page. An event loop is a good choice for
browser-based interaction because it is difficult to predict when
these events will happen. Node.js applies this approach to server-
side programming, particularly within the context of networks and
I/O operations. Node.js is often referred to as a network
programming framework because it is designed to deal with the
unpredictability of data flowing around networks. It is JavaScript’s
event loop and using callbacks that facilitates this, allowing
programmers to write asynchronous code that responds to network
or I/O events.
As you have seen with the blocking and non-blocking examples in
this hour, using an event loop is a different way of programming.
Some developers refer to it as writing programs inside out, but the
idea is that you structure your code around events rather than an
expected order of inputs. Because the event loop is based on a single
process, there are some rules that you should follow to ensure high
performance:
• Functions must return quickly.
• Functions must not block.
• Long-running operations must be moved to separate processes.
In this context, some programs are not suitable for event loops. If a
program or function needs a long time to run to complete
processing, an event loop is not a good choice. Examples of where
Node.js is not a good fit include crunching large amounts of data or
long-running computations. Node.js is designed to push data around
networks—fast!
SUMMARY
You covered a lot in this hour! You learned about callbacks and how
these work in JavaScript. You then saw how Node.js uses callbacks
and some examples of Node.js using callbacks when reading a file
from disc and fetching a home page from the web. You explored the
difference between synchronous and asynchronous programming
and were introduced to the difference between blocking and non-
blocking code. Finally, you learned about the event loop that allows
callbacks to be registered and run at a later time when an event
happens.
This hour has been heavy on theory, but you will see in future hours
that learning how Node.js uses callbacks and the general philosophy
of asynchronous programming is central to Node.js as a technology.
Q&A
Q. What about if I want to control the order of things when using
callbacks?
A. You can nest a function with a callback within another so that,
when the first callback is fired, it calls the next function. A good
example of this is getting some data from a database and then doing
something with the data with another function that involves a
callback.
Q. Aren’t threads the best way of doing this?
A. Many programmers coming to Node.js will have experience of
using threads to address the same problems that Node.js is trying to
solve. Node.js is opinionated about the way it approaches network
programming, using callbacks, the event loop, and a single process.
Some third-party modules have been created to make using threads
and fibers possible, but the core philosophy of Node.js is to try to
program within the context of an event loop and a single process.
Q. Can I program synchronously in Node.js?
A. Many developers coming to Node.js are used to programming in
other languages and find switching to using asynchronous
programming a mind shift. If you are feeling this way, give it some
time and explore what using an event loop and programming in an
asynchronous style gives to you. Node.js is opinionated that you
should use the asynchronous style of programming, so if you insist
on using a synchronous style, you are using it in a way that it was not
designed for.
Q. Isn’t this a lot more complicated and more code?
A. It’s true that using the asynchronous style of programming will
lead to more code and more complexity than coding synchronously.
Node.js is trying to solve a complex problem, though!
WORKSHOP
This workshop contains quiz questions to help cement your learning
in this hour.
Quiz
1. What are the synonyms for blocking and non-blocking code that
are often used in Node.js?
2. Why is asynchronous code a good approach for working with
networks?
3. What are some of the problems that you might run into when
using callbacks?
Quiz Answers
1. The synonyms for blocking and non-blocking code are
synchronous and asynchronous.
2. Networks are often beyond the control of a developer. You may be
fetching code from a remote server that you do not own and dealing
with many elements that are beyond your control. By using an
asynchronous style, you can allow your script to respond when a
network event returns.
3. You may run into issues around control flow when using
callbacks. This is where you need to specify the execution order of
callbacks. You may also find that you have nested callbacks that
become four or five levels deep. The goal of this hour is to
understand callbacks, but as your experience with Node.js grows,
you will come across deeply nested callbacks and control flow issues.
EXERCISES
1. Return to the jQuery example in this hour and ensure that you
understand how callbacks work. Next, look at the Node.js Hello
World web server on the Node.js home page (http://nodejs.org).
Point out to yourself where and how a callback is being used.
2. Think about the world of teaching and how this can be
synchronous and asynchronous. Think about how a class of students
attending a physical class with a teacher is a synchronous event.
Next, think about how distance learning is asynchronous. Remote
students can watch videos and use online resources to learn
whenever they want to and individuals are able to learn
asynchronously. Try to think of some other examples outside
programming that are synchronous and asynchronous.
3. To gain a deeper understanding of event loops within the context
of JavaScript, watch Douglas Crockford’s lecture on Loopage. It is
available to watch for free
at http://www.yuiblog.com/blog/2010/08/30/yui-theater-douglas-
crockford-crockford-on-javascript-scene-6-loopage-52-min/.
Hour 5. HTTP
node server.js
HTTP/1.1 200 OK
Connection: keep-alive
This shows any client requesting a page from this server that
• The version of HTTP in use is 1.1.
• The response code is 200, indicating a successful response.
• The connection is persistent in line with the HTTP 1.1 protocol.
Persistent connections are available from HTTP 1.1 and enable many
real-time capabilities.
By the Way: HTTP Headers Have Lots of Information!
You can gather a lot of information from HTTP headers. Here are the HTTP
headers from the BBC website:
HTTP/1.1 200 OK
Date: Sat, 12 Nov 2011 14:27:37 GMT
Server: Apache
Set-Cookie: BBC
-
UID=b47e9b3e289245d93524ac809080b1af9b1380f9b010a01c72c993ff199
2dc4f0curl%2f
7%2e21%2e4%20%28universal%2dapple%2ddarwin11%2e0%29%20libcurl
%2f7%2e21%2e4%20
OpenSSL%2f0%2e9%2e8r%20zlib%2f1%2e2%2e5; expires=Wed, 11-Nov-15
14:27:37 GMT;
path=/; domain=bbc.co.uk;
Vary: X-Ip-is-advertise-combined
Etag: "1321108048"
X-Lb-Nocache: true
Cache-Control: private, max-age=60
Age: 9
Content-Length: 136556
Content-Type: text/html
From the headers, it is possible to see that the web server is Apache, the
content being sent is HTML, the date it was sent, and more.
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: keep-alive
If you are Chrome user, the HTTP Headers extension works very
well. You can install the extension at: http://bit.ly/MolcMd.
Try It Yourself
Follow these steps to use the HTTP Headers extension for Chrome:
1. Install the HTTP Headers extension for Chrome.
2. Ensure that your Node.js server is running:
node server.js:
If you are a Firefox user, the Live HTTP Headers Add-On for the
Firefox web browser is useful. You can install the add-on
at: http://bit.ly/LdJBSW.
Try It Yourself
Follow these steps to use the Live HTTP Headers Firefox add-on:
1. Open Firefox and install the Live HTTP Headers add-on for Firefox
at http://bit.ly/LdJBSW.
2. In Firefox, select the Tools menu, and then select Live HTTP Headers. You
should see the live headers window.
3. Ensure that your Node.js server is running:
node server.js
cURL
If you are on a UNIX type system and are comfortable using the
terminal, you can use cURL to retrieve headers. cURL ships with OS
X and is available on most Linux distrubutions.
Try It Yourself
Follow these steps to use cURL to examine response headers:
1. Ensure that cURL is available on your system by opening your terminal
and typing curl. If it is installed, you should see
node server.js
3. In your terminal, run the following command:
curl -I 127.0.0.1:3000
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: keep-alive
A Redirect in Node.js
Using the techniques learned in this hour, it is easy to create a
simple server that redirects visitors to another web page.
The criteria for a redirect are as follows:
• Send the client a 301 response code, telling the client that the
resource has moved to another location.
• Send a Location Header to tell the client where to redirect to. In
this case, we direct visitors to Strong Bad’s home page (see Listing
5.3).
Listing 5.3. A Redirect with Node.js
This example uses the writeHead method again, but this time, a 301
response code is sent. A location header is also set to tell the client
where to redirect to.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour05/example03.
Follow these steps to create a 301 redirect:
1. Copy the code in Listing 5.3 into a file on your system called server.js.
2. Start the server by running:
node server.js
node
2. Type the following to require the URL module and assign it to a variable:
3. Type the following, which you will use to simulate an example request
URL:
var requestURL = 'http://example.com:1234/pathname?query=string#hash'
4. Now, you can start to parse the request URL and extract pieces of it. To
get the hostname, type
url.parse(requestURL).hostname
url.parse(requestURL).port
url.parse(requestURL).pathname
Using your new knowledge of the URL module, you can modify the
simple server that you created earlier to respond to different
requests with different responses by parsing the URL requested
(see Listing 5.4).
Listing 5.4. Adding Routes to Your Server
node server.js
var options = {
host: 'shapeshed.com',
port: 80,
path: '/'
};
http.get(options, function(res) {
if (res.statusCode == 200) {
console.log("The site is up!");
}
else {
console.log("The site is down!");
}
}).on('error', function(e) {
console.log("There was an error: " + e.message);
});
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour05/example05.
Follow these steps to create an HTTP client:
1. Copy the code in Listing 5.5 into a file on your system called client.js.
2. Start the server by running
node server.js
3. You should see The site is up!, indicating the site returned a 200
response. If you see anything else, there may be a problem with the site.
SUMMARY
In this hour, you learned about HTTP and how to interact with it
using Node.js. You learned how to create a simple web server and
how to send headers back to the client. You then extended your
simple web server to create a redirect and then made it respond to
more than one type of request. Finally, you created an HTTP client
to check the status of a web server.
This hour showcased the low-level features of Node.js for using
HTTP. In the next hour, you are introduced to Express, Which is a
web framework for Node.js that makes creating HTTP servers more
user friendly.
Q&A
Q. HTTP looks complicated. Do I need to understand HTTP to use
Node.js?
A. You can get by in Node.js without completely understanding the
HTTP protocol. In essence, HTTP just defines how a server and
client interact to send and receive information, so the more you
create HTTP clients or servers, the more you will understand HTTP.
Especially if you are creating web applications, understanding the
HTTP protocol will help you a great deal in your development.
Q. I’ve heard of the Express framework in Node.js. Do I really want
to write servers using the HTTP module?
A. If you are getting started, Express lowers the barriers to creating
a web server with Node.js. It takes care of many common scenarios,
like routing and templates, and is recommended for creating most
web applications. Understanding how the HTTP module works is
useful, though. There may be scenarios where you want to create a
small web server that does not need a framework, like Express.
Understanding what is going on under the hood is always a good
idea, too.
Q. How does Apache or Nginx fit with Node.js?
A. Apache and Nginx are popular, mature web servers. It is entirely
possible to proxy web traffic from Apache or Nginx to a Node.js
server. Equally you can serve traffic directly from a Node.js server.
Q. I created a Node.js server that I want to deploy already! How do I
do that?
A. Don’t worry! You learn about deployment in Hour 11, “Deploying
Node.js Applications”!
WORKSHOP
This workshop contains quiz questions to help cement your learning
in this hour.
Quiz
1. What does a 200 HTTP response mean?
2. What information can be sent in HTTP headers?
3. Why is the Connection: Keep-Alive header important?
Quiz Answers
1. A 200 response code means that the request has succeeded. A
detailed specification of possible response codes can be found
at http://bit.ly/KD3qC8.
2. You can send a large amount of information in HTTP headers,
including acceptable character sets, authorization credentials, the
date, and the user agent. For a full list, see http://bit.ly/KWge56.
3. The Connection: Keep-Alive header allows a client and a server to
keep a persistent connection open. This enables real-time
communication between the client and the server, introducing all
kinds of possibilities.
EXERCISES
1. Using Listing 5.1, change the response code to 501. Use one of the
tools discussed in this hour to check that the HTTP response code is
501.
2. Using Listing 5.4, extend the web server to include more routes.
Experiment with sending different response codes and content for
different routes.
3. Using Listing 5.5, change the options object to check the status of
different websites or servers.
Hour 6. Introducing Express
INSTALLING EXPRESS
You can install Express via npm:
express express_example
node app.js
4. Open your web browser of choice and browse to http://127.0.0.1:3000.
You see a basic website served from Express (see Figure 6.2).
EXPLORING EXPRESS
If you look in the folder for the example Express site that you just
created, you see the following structure:
• app.js
• node_modules
• package.json
• public
• routes
• views
app.js
app.js is the application file used to start the application. It contains
configuration information for the application.
node_modules
node_modules holds any node modules that are defined in
package.json and have been installed.
package.json
package.json gives information on the application, including the
dependencies that should be installed for it to run.
public
The public folder serves the application to the Web. You will find
stylesheets, javascripts, and images in this folder. You will not find
any application logic in this folder; this is a common pattern to
ensure web application security.
routes
In simple terms, a route defines the pages that an application should
respond to. For example, if you want to have an About page in your
application, you need to set up an 'about' route. The routes folder
holds these declarations. Routing is covered in more depth in Hour
7, “More on Express.”
views
The views folder defines the layouts for the application.
By the Way: Folder Structure Is Optional
The Express generator creates a suggested layout for an Express project. This
is just a suggestion, so if your application has specific requirements or your
personal preference is different, you can structure Express projects however
you want. If you are getting started with Express, it is recommended that you
use the structure from the generator.
INTRODUCING JADE
Looking inside the views folder for the example project, you see a
number of files with the .jade extension. Express makes use of
template engines to compile views to HTML. By default, Express
uses Jade as the template engine.
By the Way: Template Engines Generate HTML
Template engines are used in most web frameworks to generate HTML and
are typically used within a views folder. They allow developers to output data
from an application to HTML. Typical features include variables and loops.
Template engines can also be known as template processors or filters. Two
popular template engines are Smarty (PHP) and ERB (Ruby).
Jade is an indentation-based template engine. To understand this,
compare HTML and how it is represented in Jade (see Listing 6.1).
Listing 6.1. HTML and Jade Comparison
<div class="wrapper">
<h1>My holiday in Prague</h1>
<p>I had a great holiday in Prague where I met some great
people.</p>
<img src="images/photo.jpg" alt="Me on holiday!" />
</div>
.wrapper
h1 My holiday in Prague
p I had a great holiday in Prague where I met some great people
img(src='images/photo.jpg', alt='Me on holiday')
html
compiles to
<html></html>
You can use any HTML tag here (body, section, p, and so on).
To add an id to a tag, append a # symbol and then the name of your
id. Note that spaces are not allowed.
section#wrapper
compiles to
<section id="wrapper"></section>
You can add a class by appending a dot followed by the name of your
class.
p.highlight
compiles to
<p class="highlight"></p>
section#wrapper.class-name
compiles to
p.first.section.third.fourth
compiles to
p
span
compiles to
<p><span></span></p>
To add text within a tag, simply add it after the tag declaration.
compiles to
Jade also supports large bodies of text by using the pipe delineator:
p
| Text can be over
| many lines
| after a pipe symbol
compiles to
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour06/example02.
Follow these steps to create a page structure in Jade:
1. Open your terminal and generate a skeleton Express site by running the
following command:
express jade_structure
2. Enter the directory that you created and install the required
dependencies:
node app.js
h1= title
p Welcome to #{title}
section#wrapper
h2 Basic Structure
section
p
span This is a span within a p within a div!
7. Reload the page and check the HTML on the page by viewing the source.
Although the HTML will be compressed, you should see the following
HTML:
<section id="wrapper">
<section>
<p>
<span>This is a span within a p within a div!</span>
</p>
</section>
</section>
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour06/example03.
Follow these steps to use variables with Jade:
1. Open your terminal and generate a skeleton Express site by running the
following command:
express jade_variables
2. Enter the directory that you created and install the required
dependencies:
3. Start the application, change into the jade_variables folder, and then run
node app.js
6. Reload the page in your browser. Check the HTML on the page by viewing
the source. You should see the following HTML:
Loops
Loops allow you to iterate over arrays and objects. If you are creating
anything other than a basic brochure site, you will find yourself
using loops a lot. This is commonly known as iteration, meaning that
you iterate over an array or object and do the same thing over and
over again.
Perhaps because this is such a common pattern, Jade makes using
the minus sign optional. Admittedly, it is confusing that you use a
minus sign to specify a variable but not a loop. In these examples,
the minus sign is included before each loop for clarity, although it is
optional.
compiles to
<p>Sally</p>
<p>Joseph</p>
<p>Michael</p>
<p>Sanjay</p>
If you prefer to use the for keyword, this can also be written as
compiles to
<li>first_name: George</li>
<li>surname: Ornbo</li>
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour06/example04.
Follow these steps to use loops with Jade:
1. Open your terminal and generate a skeleton Express site by running the
following command:
express jade_loops
2. Enter the directory that you created and install the required
dependencies:
3. Start the application, change into the jade_loops folder, and then run
node app.js
6. Reload the page in your browser. Check the HTML on the page by viewing
the source. You should see the following HTML:
<ul>
<li>John</li>
<li>Paul</li>
<li>Ringo</li>
<li>George</li>
</ul>
Conditions
express jade_conditions
2. Enter the directory that you created and install the required
dependencies:
3. Start the application, change into the jade_conditions folder, and then run
node app.js
- raining = true
- if (raining)
p It is raining. Take an umbrella!
- else
p No rain. Take the bike!
6. Reload the page in your browser. Check the HTML on the page by viewing
the source. You should see the following HTML:
- raining = false
8. Reload the page in your browser. Check the HTML on the page by viewing
the source. You should see the following HTML:
Inline JavaScript
script
alert('You can execute inline JavaScript through Jade')
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour06/example06.
Follow these steps to execute inline JavaScript in Jade:
1. Open your terminal and generate a skeleton Express site by running the
following command:
express jade_inline_javascript
2. Enter the directory that you created and install the required
dependencies:
node app.js
script
alert('Inline JavaScript in Jade')
6. Reload the page in your browser. You should see a JavaScript alert.
Includes
Most websites have parts of the page that appear on every page of
the site. Example of these include
• Headers
• Footers
• Sidebars
Did You Know?: Includes Make It Easier to Maintain a Website
Includes move common parts of a website into single files. This means that
when a client asks for an extra item to be added to a header, you only have to
change a single file.
Jade supports includes with the include keyword and then the name
of the template that you want to include.
html
body
include includes/header
express jade_includes
2. Enter the directory that you created and install the required
dependencies:.
3. Start the application, change into the jade_includes folder, and then run
node app.js
Mixins are a feature of Jade that not many other template engines
have. If you find yourself repeating the same blocks of code over and
over, using mixins is a good way to keep your code maintainable and
clean. Think of mixins as includes for your code. An example of
where you may want to use a mixin is outputting data in a loop. To
define a mixin, use the mixin keyword:
mixin users(users)
ul
each user in users
li= user
When the mixin is defined, you can use and reuse it in your
templates:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour06/example08.
Follow these steps to use mixins in Jade:
1. Open your terminal and generate a skeleton Express site by running the
following command:
express_jade_mixins
2. Start the application, change into the jade_mixins folder, and then run
node app.js
mixin users(users)
ul
each user in users
li= user
- users = ['Tanya', 'Jose', 'Kim']
mixin users(users)
- more_users = ['Mark', 'Elena', 'Dave', 'Pete', 'Keiron']
mixin users(more_users)
5. Reload the page in your browser. You should see two lists of users
generated from a single mixin.
SUMMARY
In this hour, you learned about Express, a web framework for
Node.js. You learned about when you might choose to use a web
framework and what it can offer you.
You learned about Jade, a template engine for Node.js, and how to
use it to output variables, loops, conditional statements, inline
JavaScript, includes, and mixins.
After this hour, you are able to create basic sites with Node.js and
Express and begin to display data within your applications.
Q&A
Q. Should I always use Express with Node.js or just sometimes?
A. Express is a web framework for Node.js, so there are times when
it is not appropriate. If your requirements are to work with views
and show data within a web browser Express is a good fit. If you are
building a command-line script with Node.js or a tiny web service,
Express is probably not the right fit. In all cases, pick the best tool
for the job.
Q. I’m not keen on indentation-based template engines. Are there
any others?
A. Although Express uses Jade as the default template engine, it is
agnostic to the template engine that is used. Another popular choice
for Express is the EJS (Embedded JavaScript Templates) Template
Engine. This is not indentation based and is close to how ERB works
in Ruby. You may also use jQuery templates with Jade. New
template engines are appearing all the time, many with support for
Express. Check the Node.js wiki for a comprehensive
list: https://github.com/joyent/node/wiki/modules#wiki-
templating.
Q. What about performance?
A. Jeremy Ashkenas created a test to show the performance of
different JavaScript template languages. If you are interested in the
performance of template languages, the post gives a benchmark and
performance comparisons (http://bit.ly/KKa3nw).
Q. What are the pros and cons of using a template engine like Jade?
A. Using a template engine allows you to develop more quickly and
take advantage of built-in checks for your code. With indentation,
Jade is easy to read and hence maintain. In terms of cons, Jade adds
a layer of complexity by abstracting plain HTML that, for very
simple projects may be overkill.
WORKSHOP
This workshop contains quiz questions to help cement your learning
in this hour.
Quiz
1. Why might you want to use a framework over Express instead of
writing your own code?
2. Where do you place any dependencies for an Express project?
3. Does Express have a set folder structure?
Quiz Answers
1. For many web applications, there are common patterns, such as
templates and routes. You can write your own code for this, but
many developers avoid this work by using and contributing to
frameworks, like Express.
2. You place dependencies for an Express project in the package.json
file, just as you would any other Node.js project.
3. No. Although the Express generator produces a folder structure
for you, there is no requirement to follow the suggested structure.
EXERCISES
1. Create an Express site using the command-line generator. Explore
each of the folders and understand what they do. Refer to the notes
in this hour on folder structure.
2. From the terminal, run express --help. Note how it is possible to
change how the generator works by passing in arguments when
generating new Express sites. Try switching the template engine
used when you generate an Express site.
3. Generate a new Express site and practice your understanding of
using Jade by assigning a variable and then outputting it. Create an
array of data and then use a loop to show the data. Finally, create a
header include file and use it in your template.
Hour 7. More on Express
What You’ll Learn in This Hour:
• How routing works in Express
• Adding routes in Express
• Using parameters in routes
• Passing data to the view layer
• Displaying data in the view layer
ROUTING IN WEB APPLICATIONS
Routing describes whether and how an application should respond
to certain Hypertext Transfer Protocol (HTTP) requests. Your
browser makes these requests when you are interacting with an
application or website.
Routing is just a term for defining the end points for HTTP requests
in an application. So, if you want your application to be able to do
something, you need to set up a route for it!
HOW ROUTING WORKS IN EXPRESS
Express uses HTTP verbs to define routes. HTTP verbs describe the
type of request that is made to a server. The most common ones that
you are likely to use are
• GET—This retrieves data from a server.
• POST—This sends data to a server.
Other HTTP verbs include PUT, DELETE, HEAD, OPTIONS, and
TRACE.
When you generate a basic Express site, the generator expects you to
define routes for your application. To understand this, you generate
a basic Express application and examine the routes available.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour07/example01.
To generate a basic Express application and examine the available routes,
follow these steps:
1. Create a basic Express site:
express express_routing
cd express_routing
npm install
In the example, there is no route available for /about, so a Page Not Found
(404) response is returned.
By the Way: You Make GET and POST Requests All the Time!
When you load a web page, your browser is making GET requests to retrieve
HTML, CSS, JavaScript, and image files. When you submit a form, your
browser is most likely making a POST request. HTTP verbs are actually quite
simple, and if you are browsing the web, you are making GET and POST
requests all the time.
node app.js
Did You Know?: Express Takes Care of Some Routes for You
Because some routes are assumed to be needed by all applications, Express
provides some Middleware that will serve anything you add to the /public
folder. This is included if you use the site generator. Imagine how tedious it
would be to add a route for every single image that you want to show! So, you
can safely assume that anything you put into the public folder will be served
and does not need a route declaring for it.
express express_post
cd express_post
npm install
2. Add a form to the index view by opening index.jade and adding the
following code:
h2 Form example
form(method='post', action='/')
fieldset
legend Add a user
p
label First name
input(name='user[first_name]')
p
label Last name
input(name='user[surname]')
p
input(type='submit', value='Save')
node app.js
/users/12
/projects/2345
Anything that comes after the forward slash following user in the
request will be captured and made available to the application
through the req object. So, if you do a GET request to /user/24,
Express extracts that number and makes it available for the
application to use within req.params.id. The symbol that you specify
in the route becomes the params key, so if you were to
use '/users/:name', it would be accessible by req.params.name.
At this point, a web application normally fetches the data for the
user with the id and returns it.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour07/example04.
Follow these steps to use parameters in routes:
1. Create a basic Express site:
express express_parameters
cd express_parameters
npm install
2. Add the following to the app.js file within the routes section:
node app.js
If you used the Express generator, look inside the routes folder; you
see a single file index.js that contains the route declarations:
/*
* GET home page.
*/
Just like the GET and POST requests we have already seen, this
defines how the application should respond to a request. The
difference is that the function is assigned to a variable that can be
used elsewhere. By using the require statement and assigning it to a
variable, it becomes available to the main application file
as routes.index.
Having required the routes file in the main application file app.js, it
then becomes possible to declare the route to the home page
using routes.index:
app.get('/', routes.index);
How you organize your routes files is not enforced, but you see a
typical pattern of dividing up routes by resources in Hour 8,
“Persisting Data.”
Separating routes into their own files is a good idea because
• It makes code more maintainable and readable.
• You can use version control to quickly view the history of a single
file.
• It supports future growth of the application while keeping it
maintainable.
VIEW RENDERING
You will have noticed that you have been
using res.render and res.send to specify what is sent as the response.
View rendering describes how and what the application should
render in response to an HTTP request.
Examples of responses that you might use for view rendering include
• Sending HTML
• Sending JSON (JavaScript Object Notation) data
• Sending XML (Extensible Markup Language) data
All these are specified within the response section of a route.
Most of the time, you will use res.render. It is powerful and allows
you to
• Render templates, including different template engines.
• Pass local variables to templates.
• Use a layout file.
• Set the HTTP response code.
A common pattern is to use res.render within a route to render a
template and to pass local variables to it:
h1 Accessing data
h3 First Name
p= user.first_name
h3 Surname
p= user.surname
express express_locals
cd express_locals
npm install
2. Open the app.js file and remove the line that reads
app.get('/', routes.index);
3. After the // Routes comment in the app.js file, add the following code:
4. Open the views/index.jade file and replace the contents with the following
code:
h3 First Name
p= user.first_name
h3 Surname
p= user.surname
h3 Address
p= user.address
h3 Facebook Friends
p= user.facebook_friends
node app.js
SUMMARY
In this hour, you learned more about Express. In particular, you
learned about routing and how it works in Express. You learned how
to add GET and POST requests to your Express applications and
how to use parameters to show content dynamically. Finally, you
learned how to pass data through to the view layer.
Now, you have a great foundation to build Node.js applications using
Express!
Q&A
Q. Where can I learn more about HTTP verbs?
A. If you want to read the specification on HTTP verbs, you may do
so here: http://bit.ly/Opx29E.
Q. Should I use the Express generator?
A. While you are getting started, using the Express generator is a
great way to start developing. Over time, you may have more
opinions about how to structure your application. Express offers you
a suggested structure, but you do not have to stick to it!
Q. Are other frameworks available for Node.js?
A. A number of frameworks are available for Node.js. These include
Geddy (http://geddyjs.org/), Tako
(https://github.com/mikeal/tako), and Flatiron
(http://flatironjs.org/).
WORKSHOP
This workshop contains quiz questions to help cement your learning
in this hour.
Quiz
1. If you create a POST route to receive a form, will it save to a
database?
2. What are the advantages of using parameters to set the data
shown in routes?
3. When should you move routes to a separate file?
Quiz Answers
1. No. Creating a route for a POST request does nothing more than
make sure the application can receive a POST request. You need to
set what you want to do with the data received. Typically, this would
be to validate the data and then save it somewhere. Data submitted
by a form will not be saved anywhere by creating the route.
2. Using parameters allows you to reuse templates and change
content based on one or more parameter. This makes your code
maintainable and makes it simple to display thousands of different
records. Using this approach, a relatively small code base can power
many, many different pages.
3. There is no right answer to this, but maintenance is the key thing
to consider here. If your application is three or four routes, you are
unlikely to need a separate file for your routes. Once you get above
that, it makes sense for your routes to be moved into separate files.
It is up to you, but always think of how readable and maintainable
your code is for another developer.
EXERCISES
1. Create an Express site that includes GET requests for /about,
/contact, and /products.
2. Create an Express site with a single / route that does not use a
layout file.
3. Create an Express site that uses parameters to show different
content depending on a GET request parameter. Using the following
example data, your application should display Keyser Soze’s details
at /users/1 and Roger Kint’s details at /users/2. One way of solving
this can be found in hour07/example06 of the code examples.
var users = {
1:{
first_name: 'Keyser',
surname: 'Soze',
address: 'Next door',
facebook_friends: '4'
},
2:{
first_name: 'Roger',
surname: 'Kint',
address: 'London, England',
facebook_friends: '10000000000000'
}
}
var fs = require('fs'),
data = "Some data I want to write to a file";
fs.writeFile('file.txt', data, function (err) {
if (!err) {
console.log('Wrote data to file.txt');
} else {
throw err;
}
});
The File System module is required on the first line and then a
variable is set to hold the data that should be written to the file.
The writeFile method is then used to write the data to a file.
Points to note here are
• The writeFile method also creates the file for you if it does not exist.
There is no need for you create it.
• The file will be written to a location relative to where the script is
run. If you need to control exactly where the file is written, you may
specify the full path (e.g., /full/path/to/file.txt).
• Errors can include the file not existing or not having permissions to
read the file.
Some scenarios where you may want to write to a file to store data
include
• To back up data to a file
• To store timestamps so other scripts can use them
• To track the Process Identifier (PID) of a process
• To log information about a script or application
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example01.
Follow these steps to write data to a file:
1. Copy the code in Listing 8.1 into a file on your system called app.js.
2. Run the script:
node app.js
3. In the same folder as your app.js file, you should see a new file (‘file.txt’)
has been created.
4. Open the file.
5. It should contain “Some data I want to write to a file.”
var fs = require('fs');
fs.readFile('file.txt', 'utf8', function (err, data) {
if (!err) {
console.log(data);
} else {
throw err;
}
});
If you run the script, it reads the contents of the file and outputs it to
the console.
node app.js
Hello Node.js!
As in the example where you wrote to a file, the File System module
is required on the first line, but this time, the readFile method is
used. This takes a file and a character encoding as arguments.
Points to note here are
• The location of file.txt is relative to where the script was run. You
may also specify the full path—for example, /full/path/to/file.txt.
• If you do not provide a character encoding, the raw buffer will be
returned.
• Possible errors include not having permission to read the file or the
file not existing.
Watch Out: Encoding Is Important!
Data on computers is stored and transmitted as bits. A bit is a unit of
information on a computer that it is not human readable. When a computer
outputs text, it converts bits to characters using character encoding to make
it human readable. Character encoding defines how a computer should map
bits to certain character sets. The de facto character set for encoding on the
Web is UTF-8, so if in any doubt, always use that.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example02.
Follow these steps to read data from a file:
1. Copy the code in Listing 8.2 into a file on your system called app.js.
2. In the same directory, create a file called file.txt.
3. Add some text into the file.
4. Run the script:
node app.js
5. The script reads the file and outputs the text you wrote in file.txt.
SET SOMETHING='12345678'
export SOMETHING='12345678'
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example03.
Follow these steps to read environment variables:
1. Create a file called app.js and copy the following code into it:
console.log(process.env.SECRET_KEY)
2. Set an environment variable of SECRET_KEY. If you are on a UNIX type
system, run
export SECRET_KEY='c6YFPvdT7Yh3JAFW62EBa5LDe4X'
If you are on a Windows type system, run
SET SECRET_KEY='c6YFPvdT7Yh3JAFW62EBa5LDe4X'
3. Run the script:
node app.js
4. You should see c6YFPvdT7Yh3JAFW62EBa5LDe4X printed to the terminal.
USING DATABASES
The examples in this hour have shown how to set and retrieve small
amounts of data using files or environment variables. If you are
creating web applications with Node.js, you will definitely want to
use a database or data store at some point. Some of the scenarios
that you need a database for include
• Allowing users to save information
• Allowing users to create, read, update, and delete records
• Creating a web service, or API, from your data
Node.js has good support for most major databases, so which one
should you use?
Relational Databases
You may have had exposure to some of the following relational
databases:
• MySQL
• PostgresSQL
• Oracle
• Microsoft SQL Server
• SQLite
Relational databases store data in separate tables and create
relationships between tables using primary and foreign keys. Let’s
say you have a blogging application with a database including tables
for blog posts and comments. Using primary and foreign keys within
the tables, it is easy to write queries for things like
• The last 10 blog posts in date order
• All of the comments for a blog post
• All blog posts between two moments in time
A common approach with relational databases is to join tables
together to create views onto data. This can be powerful, because it
keeps data separate so it can be queried in any way, but also
provides complex views into the data. If you are building a web
application that creates relationships between data that fits neatly
into a table structure, then relational databases are a great option.
Did You Know?: Search npm for Database Libraries
You can search npm to find libraries for connecting to your database of
choice. These libraries are written and maintained by developers around the
world. Most major databases are fully supported.
NoSQL Databases
NoSQL is a term that has emerged in recent years to cover a broad
range of databases that do not conform to the Relational Database
model. Some databases that are grouped into the NoSQL bracket are
• Cassandra
• Redis
• Memcached
• MongoDB
• Hadoop
The feature sets for these databases can vary wildly but generally do
not require fixed table schemas, avoid using joins, and are designed
to scale horizontally. Because Node.js is a disruptive technology,
many use cases match using NoSQL databases very well.
The database you choose is a personal choice based on the type of
application you will be creating. If you are just creating basic
applications, you should probably pick a database that you are
familiar with.
USING MONGODB WITH NODE.JS
A popular choice in the Node.js community is MongoDB. MongoDB
is a document-oriented database that does not follow the relational
model of joining relational data together. It can perform most of the
features of a relational database and is designed to be highly
available and scalable.
To learn how to use MongoDB with Node.js, you will be creating a
todo application with Express that allows a user to create, read,
update, and delete todo items.
The final application looks like what’s shown in Figure 8.1.
Figure 8.1. The completed todo application
Connecting to MongoDB
Mongoose (http://mongoosejs.com/) is a fully featured third-party
module for working with MongoDB in Node.js. You use Mongoose to
interact with MongoDB in the example todo application to save todo
items.
To add the Mongoose module to a project, include it as a
dependency in the project’s package.json file:
{
"name": "your-application",
"version": "0.0.1",
"dependencies": {
"mongoose": ">= 2.3.1"
}
}
Do not forget to run npm install after you have added it to install it
into your project!
After you install Mongoose, it must be required in the application
file:
mongoose.connect('mongodb://localhost/your_database');
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example04.
Follow these steps to connect to MongoDB:
1. Create a basic Express site:
express connect_to_mongo
cd connect_to_mongo
npm install
2. Open the package.json file and add Mongoose as a dependency. The file
should look like this:
{
"name":"application-name",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.1",
"jade":">= 0.0.1",
"mongoose":">= 2.3.1"
}
}
3. Run npm install to install the dependencies to your application.
4. Open the app.js file and require mongoose in the application in the section
at the top of the file:
var express = require('express'),
routes = require('./routes'),
mongoose = require('mongoose');
5. Immediately after the preceding section, add a line to connect to
MongoDB. Note you are specifying the database as 'todo_development'.
There is no need to create the database:
mongoose.connect('mongodb://localhost/todo_development', function(err)
{
if (!err) {
console.log('connected to MongoDB');
} else {
throw err;
}
});
6. Make sure that MongoDB is running on your machine and then start the
server by running
node app.js
7. In the console, you should see
Express server listening on port 3000 in development mode
connected to MongoDB
Defining Documents
In MongoDB, there is no concept of tables as in relational database.
Instead, MongoDB structures data around the idea of documents.
This is a little disjointing at first, but will soon become familiar. A
document in MongoDB has attributes. For example, if you had a dog
document, it might have the following attributes:
• Name
• Breed
• Color
• Age
For your todo application, you define a task document. This is
simple: It just has a single attribute of 'task'.
To define a document in MongoDB with the Mongoose module, the
process is to go through the Schema interface provided by Mongoose
and then declare the attributes. When using Mongoose, you may be
confused between MongoDB’s documents and Mongoose’s models.
Mongoose provides some additional functionality, such as
validators, getters, and setters, which is wrapped up in the concept
of a model. Ultimately, however, a model maps to a MongoDB
document.
Depending on the type of data you want to use in your attributes,
Mongoose allows you to declare the following types:
• String
• Number
• Date
• Boolean
• Buffer
• ObjectID
• Mixed
• Array
For the tasks model, a single attribute is needed and looks like this:
Notice that there are two declarations of the variable 'Task'. The first
is to define the schema for the model; the second is so that the
variable can be used to create new tasks. Admittedly, this pattern
does not seem elegant, and you may choose to have separate names
for the variables.
In the model declaration, the task is declared as a String. This tells
MongoDB what type of data to expect. A string is a sequence of
characters, exactly the right fit for a task like 'Feed the dog' or 'Eat
Lunch'.
You do not need to run any database migrations or create Structured
Query Language (SQL) scripts to set up the database. This is the only
thing you need to do; so with just a few lines of code, you have
defined your data structure and are ready to start building the views
in your application!
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example05.
Follow these steps to define a Mongoose model:
1. Return to the example Express application that you created to connect to
MongoDB.
2. Open app.js and declare your todo model. Replace the block starting
with 'mongo.connect' with the following code:
mongoose.connect('mongodb://localhost/todo_development')
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
!!
html
head
title= title
link(rel='stylesheet',
href='http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css')
body
section.container!= body
h1 Your Tasks
Hopefully, this pattern is familiar to you from Hour 7! The next step
is to fetch the todo items from MongoDB so that you can display
them. Through the Mongoose module, there is an easy way to fetch
all records stored in a model:
YourModel.find({}, function (err, docs) {
// do something with the data here
});
The tasks index route can now be updated to query MongoDB and
pass the results through to the view layer:
Note that docs is passed through to the view layer and contains any
records that have been returned from MongoDB. The view file at
/views/tasks/index.jade can now be updated to show these results, if
there are any.
h1 Your tasks
- if(docs.length)
table
tr
th Task
each task in docs
tr
td #{task.task}
- else
p You don't have any tasks!
The Jade template checks to see whether there are any tasks by
calling length on docs. If there are no tasks, this is returned as false
and the template tells the user that they do not have any tasks. If
there are documents, then a loop outputs each one.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example06.
Follow these steps to display data from MongoDB:
1. Return to the example Express todo application that you have been
working on.
2. Open app.js and add the tasks route. This fetches all tasks from
MongoDB:
app.get('/tasks', function(req, res){
Task.find({}, function (err, docs) {
res.render('tasks/index', {
title: 'Todos index view',
docs: docs
});
});
});
3. Create a new file at views/tasks/index.jade and add the following content:
h1 Your tasks
- if(docs.length)
table
tr
th Task
each task in docs
tr
td #{task.task}
- else
p You don't have any tasks!
4. Start the server using
node app.js
5. Browse to http://0.0.0.0:3000/tasks.
6. You should see a page saying you have no tasks (see Figure 8.5). You need
to add a way of adding tasks!
The corresponding view files show a form to allow the user to submit
a task. As forms are being styled with Twitter Bootstrap, the
elements have class names to apply the correct styling:
form(method='post', action='/tasks')
fieldset
legend Add a task
div.clearfix
label Task
div.input
input(name='task[task]', class='xlarge')
div.actions
input(type='submit', value='Save', class='btn primary')
button(type='reset', class='btn') Cancel
Within this form, there is a single field that sends the data as a POST
request to /tasks, where the post will be received and saved to
MongoDB.
To receive the data, a new route is created that receives the POST
request and creates a task. Mongoose makes it easy to do this:
p
a(href='/tasks/new', class='btn primary') Add a Task
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example07.
Follow these steps to allow tasks to be created:
1. Return to the example Express todo application that you have been
working on.
2. Open app.js and add the new tasks route.
app.get('/tasks/new', function(req, res){
res.render('tasks/new.jade', {
title: 'New Task'
});
});
3. Create a new file at views/tasks/new.jade and add the following content:
h1 New task view
form(method='post', action='/tasks')
fieldset
legend Add a task
div.clearfix
label Task
div.input
input(name='task[task]', class='xlarge')
div.actions
input(type='submit', value='Save', class='btn primary')
button(type='reset', class='btn') Cancel
4. Add a second new route to the app.js file to receive the POST request and
create a task:
app.post('/tasks', function(req, res){
var task = new Task(req.body.task);
task.save(function (err) {
if (!err) {
res.redirect('/tasks');
}
else {
res.redirect('/tasks/new');
}
});
});
5. Add a link to views/tasks/index.jade to allow users to create tasks:
p
a(href='/tasks/new', class='btn primary') Add a Task
6. Start the server using
node app.js
7. Browse to http://0.0.0.0:3000/tasks.
8. You should see a link to add tasks and be able to create new tasks
(see Figure 8.6).
Using findById, the record can be found and a route can be created
for the edit form. This route captures the id request parameter so the
record can be retrieved from MongoDB. Note how the route also
passes the task through to the view layer so that the existing data can
be shown to the user:
The corresponding view shows the data to the user and allows the
user to edit it. Because the form is an update, a PUT request is used.
Express has a helper that converts standard post requests into other
methods by specifying the name as '_method'. The form uses this
feature in a hidden input field to make sure that Express converts
the POST into a PUT. Express does this, as most HTML forms only
support GET and POST:
- if(docs.length)
table
tr
th Task
th
each task in docs
tr
td #{task.task}
td
a.btn(href="/tasks/#{task.id}/edit") Edit
- else
p You don't have any tasks!
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example08.
Follow these steps to allow tasks to be edited:
1. Return to the example Express todo application that you have been
working on.
2. Open app.js and add the edit tasks route:
app.get('/tasks/:id/edit', function(req, res){
Task.findById(req.params.id, function (err, doc){
res.render('tasks/edit', {
title: 'Edit Task View',
task: doc
});
});
});
3. Create a new file at views/tasks/edit.jade and add the following content:
h1 Edit task view
Deleting Tasks
The todo application can now create, read, and edit records. The
final piece of functionality is to add the capability to delete records.
First, a route is created to receive a DELETE request that uses the id
from the request to find which task to delete. The
same findById method provided by Mongoose is used to find the task
to be deleted. If no task is found for the id that is passed in, an error
is returned:
app.del('/tasks/:id', function(req, res){
Task.findById(req.params.id, function (err, doc){
if (!doc) return next(new NotFound('Document not found'));
doc.remove(function() {
res.redirect('/tasks');
});
});
});
- if(docs.length)
table
tr
th Task
th
each task in docs
tr
td #{task.task}
td
a.btn(href="/tasks/#{task.id}/edit") Edit
td
form(method='post', action='/tasks/' + task.id)
input(name='_method', value='DELETE', type='hidden')
button.btn(type='submit') Delete
- else
p You don't have any tasks!
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour08/example09.
Follow these steps to allow tasks to be deleted:
1. Return to the example Express todo application that you have been
working on.
2. Open app.js and add the delete tasks route:
app.del('/tasks/:id', function(req, res){
Task.findById(req.params.id, function (err, doc){
if (!doc) return next(new NotFound('Document not found'));
doc.remove(function() {
res.redirect('/tasks');
});
});
});
3. Update the tasks/index.jade view to include a link to the edit view:
- if(docs.length)
table
tr
th Task
th
each task in docs
tr
td #{task.task}
td
a.btn(href="/tasks/#{task.id}/edit") Edit
td
form(method='post', action='/tasks/' + task.id)
input(name='_method', value='DELETE', type='hidden')
button.btn(type='submit') Delete
- else
p You don't have any tasks!
4. Start the server using
node app.js
5. Browse to http://0.0.0.0:3000/tasks.
6. You should see a link to delete tasks and be able to delete tasks.
Adding Flash Messages
The todo application can create, read, edit, and delete! It would be
great if the application could tell users that their actions have been
successful, though. Through the Express framework, this can be
added to the application by first enabling session support.
To enable sessions within Express, the following two lines are added
within the app.configure section of the app.js file:
app.use(express.cookieParser());
app.use(express.session({ secret: "OZhCLfxlGp9TtzSXmJtq" }));
The first line tells Express to enable the cookieParser. This allows
Express to save sessions. The second line sets a secret for Express to
use to secure the session. This can be any string you want. A useful
tool is available at random.org for generating random
strings: http://www.random.org/strings/.
The common term for describing messages that notify the user of
what has happened is flash messages. Figure 8.8 shows an example
of a message shown to a user after he has created a task.
Figure 8.8. An example of a flash message
To show a flash message, a Jade template can check for the existence
of a message and show it if present. The example uses the styling
available via Twitter Bootstrap to style the alert messages.
mixin flash-messages(flash)
- if(flash.warning)
div.alert-message.warning
p= flash.warning
- if(flash.info)
div(data-alert='alert').alert-message.success
p= flash.info
This can then be reused across views. To add flash messages to the
tasks/index.jade view, the following lines are added. Now if there is
a flash message, it will be shown to the user:
function validatePresenceOf(value) {
return value && value.length;
}
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour09/example01.
Follow these steps to debug with the STDIO module:
1. Create a file called app.js and copy the following code into it:
console.log('Debugging message')
2. Run the script:
node app.js
3. You should see “Debugging Message!” in your terminal.
Using console.log() is often a quick-and-dirty solution for debugging
the following scenarios:
• Checking the value of a variable or string
• Logging that a script has called a function
• Logging responses from third-party services
If you want to log errors, you can use console.warn(), which is also
aliased to console.error(), and both of these methods print to the
standard error stream.
Did You Know?: Programs Write to Different Streams
On UNIX-type systems data streams are often joined together or piped. This
allows small, distinct programs to be combined to create more complicated
functionality. To help glue programs together, programs write to standard
input, output, and error interfaces. These are known as Standard Input
(stin), Standard Output (stout), and Standard Error (sterr). One example of
using these standard streams in use is a test runner. When a set of
automated tests pass, the output is sent to Standard Output. When tests fail,
a message is sent to Standard Error. This allows other software to
understand the results of running the tests and act accordingly. Node.js’s
STDIO module follows this convention, so make sure that you write to the
correct stream!
An example of how you might use console.error() is to log why an
error was caused. For example, if you are not checking that a
function is defined, or if a function is deleted after you have written
some code that calls the function, an error will be thrown. To make
the problem explicit, you can use console.error() within a try
catch statement to log why the error has occurred (see Listing 9.1).
function notDefined(){
try {
someFunction(); // undefined
} catch (e) {
console.error(e);
}
}
notDefined();
If you run this code, you see the following error in the terminal:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour09/example02.
Follow these steps to write errors to sterr with the STDIO module:
1. Copy the code in Listing 9.1 into a file on your system called app.js.
2. Run the script:
node app.js
3. You should see [ReferenceError: someFunction is not defined] in your
terminal, which indicates that the someFunction is undefined.
Using console.error() is useful as it provides information about the
type of error that was thrown and precisely what the problem is. In
the example, it is possible that, at a later date, the
function someFunction() will be defined, but that there will be an error
within that function. This might look like Listing 9.2.
Listing 9.2. Catching Errors with console.error()
function someFunction(){
return undefinedVar; // undefined
}
function notDefined(){
try {
someFunction(); // now defined
} catch (e) {
console.error(e);
}
}
notDefined();
If you run this code again, you will see that the error message has
changed and that it is immediately clear where the problem lies:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour09/example03.
Here’s an example to show you how to understand errors with the STDIO
module:
1. Copy the code in Listing 9.2 into a file on your system called app.js.
2. Run the script:
node app.js
3. You should see [ReferenceError: undefinedVar is not defined] in your
terminal, which indicates that the undefinedVar is undefined.
Using console.error() is a lightweight way of finding out why an error
has been thrown. For discovering where bottlenecks are in code or
quick benchmarking of parts of your code, the STDIO module
has console.time() and console.timeEnd(). Both of these methods take
an argument of a label that effectively ties the two methods together.
To start a timer, use console.time(); to end it, use console.timeEnd().
These methods are useful for benchmarking small bits of code or
anything time related. Using these methods, you can easily find out
where your code is slow or compare two ways of executing the same
code for optimum performance.
Listing 9.3. A Simple Benchmark
var sum = 0;
var arr = new Array(1000000);
console.time('for-loop-1');
for (var i in arr) {
sum += arr[i];
}
console.timeEnd('for-loop-1');
console.time('for-loop-2');
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
}
console.timeEnd('for-loop-2');
In this example, the exercise is to discover the most efficient way of
performing a simple operation on an array of a million random
numbers. Using the methods provided by the STDIO module, it is
easy to discover which way is faster by timing both loops. This can be
useful when debugging performance issues. Running the script
shows that the second option is much faster.
for-loop-1: 431ms
for-loop-2: 27ms
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour09/example04.
The following steps show performance testing with the STDIO module:
1. Copy the code in Listing 9.3 into a file on your system called app.js.
2. Run the script:
node app.js
3. You should see the execution times for the two loops outputted to the
terminal:
for-loop-1: 431ms
for-loop-2: 27ms
If you want to see a stack trace of where you are at any point during
the execution of a script, do this with the console.trace() method
(see Listing 9.4).
Listing 9.4. Viewing a Stack Trace
function notDefined(){
console.trace();
try {
someFunction();
} catch (e) {
console.error(e);
}
}
notDefined();
This prints a stack trace from the point of the code at which it was
inserted:
Trace:
at notDefined
(/Users/george/code/nodejsbook.io.examples/hour09/example05/
app.js:2:11)
at Object.<anonymous>
(/Users/george/code/nodejsbook.io.examples/hour09/example05/
app.js:10:1)
at Module._compile (module.js:432:26)
at Object..js (module.js:450:10)
at Module.load (module.js:351:31)
at Function._load (module.js:310:12)
at Array.0 (module.js:470:10)
at EventEmitter._tickCallback (node.js:192:40)
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour09/example05.
Here’s how to print a stack trace with the STDIO module:
1. Copy the code in Listing 9.4 into a file on your system called app.js.
2. Run the script:
node app.js
3. You should see the stack trace shown in your terminal.
By the Way: Stack Traces Help Debugging
A stack trace is a list of function or method calls at a point in the application.
Often, an error or bug may be in code that has been included from another
file or module. A stack trace allows a developer to read through how the
script has reached a point in execution and track down where a problem has
occurred.
debugger
Now, you can run your program with debugging by running Node.js
with the additional debug argument:
debug> repl
Press Ctrl + C to leave debug repl
>a
3
>b
5
>c
ReferenceError: c is not defined
To exit the REPL, press Ctrl+C and type cont to jump to the next
breakpoint in your code. In this example, this moves to line 7 of the
code. Once again, it is possible to query the values of a, b, and c by
entering the REPL by typing repl:
debug> repl
Press Ctrl + C to leave debug repl
>a
21
>b
7
>c
11
To jump to the final breakpoint, press Ctrl+C again and type cont.
This is the last breakpoint in the code, and entering repl, you will be
able to check the values of a, b, and c again:
debug> repl
Press Ctrl + C to leave debug repl
>a
21
>b
5
>c
ReferenceError: c is not defined
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour09/example06.
The following steps show you how to debug with the V8 debugger:
1. Copy the code in Listing 9.6 into a file on your system called app.js.
2. Run the script with the debugger enabled:
node debug app.js
3. You should see the debugger start and then halt the execution of the
script.
4. Type cont to continue to the first breakpoint.
5. Type repl and query the values of a, b, and c.
6. Press Ctrl+C and then type cont to skip to the next breakpoint.
7. Type repl and query the values of a, b, and c.
8. Press Ctrl+C and then type cont to skip to the next breakpoint.
9. Type repl and query the values of a, b, and c.
10. Press Ctrl+C and then type cont.
11. You should see “program terminated” to indicate that the program has
finished.
12. Press Ctrl+D to exit the debugger.
NODE INSPECTOR
Perhaps the most useful tool for debugging Node.js applications is
Node Inspector. This third-party tool was created by dannycoates
(Danny Coates). To use Node Inspector, you must have a WebKit
browser on your machine, so this means installing either Chrome or
Safari. Node Inspector allows you to use the Webkit JavaScript
debugger to step through your code and supports the following
features:
• Browsing source code files for your application
• Using a console to interact with your application
• Adding and removing breakpoints
• Stepping through the function calls in your code
• Stepping in and out of functions
• Setting watch expressions
• Viewing the stack trace at different points in your code
• Viewing scope variables
Providing you have a WebKit browser on your machine, you can
install Node Inspector from npm:
The process for starting Node Inspector is twofold. First, start your
application with the --debug or --debug-brk flags to enable the
JavaScript debugger. If you use --debug-brk, then Node Inspector
places a breakpoint on the first line of your application. Note that to
begin debugging, you need to press Play to jump to your first
breakpoint.
node-inspector
node-inspector
You can step through the breakpoints by hitting the Play button at
the top right-hand column. Note how the information in the right-
hand column changes as you step through and allows you to inspect
the current state of your application (see Figure 9.2).
Figure 9.2. Stepping through breakpoints
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour09/example07.
Here’s how to debug with Node Inspector:
1. Copy the code in Listing 9.7 into a file on your system called app.js.
2. Run the script, enabling the JavaScript debugger and breaking on the first
line:
node --debug-brk app.js
3. You should see “debugger listening on port 5858.”
4. In a separate terminal tab or window, start Node Inspector:
node-inpsector
5. You should see “visit http://0.0.0.0:8080/debug?port=5858 to start
debugging.”
6. Open a Webkit browser (Chrome or Safari) at
http://0.0.0.0:8080/debug?port=5858.
7. You should see your script.
8. Add breakpoints by clicking the line numbers 4, 7, 9, and 11.
9. Click the Play button to step through your breakpoints.
10. Note how the variable values change under Scope Variables in the right-
hand column.
Did You Know?: Node Inspector Is a Great Way to Learn
Another use of Node Inspector is learning about how code works in Node.js.
Node Inspector allows you to step through the execution of code and see the
files and modules that it is referencing. By using Node Inspector, reading
code can become interactive rather than just reading the code in a text
editor.
A NOTE ON TESTING
How you use a debugger depends on your style of development.
Some developers like to build an application organically. This means
they write code and continue to refresh the page or repeat expected
inputs until they are satisfied that it works. For this style of
development, Node Inspector is useful, because it gives you a view
right into the heart of your application. Most professional
developers, however, follow a test-driven development process. This
involves writing automated tests that can be run over and over again
to ensure that an application is working as expected. In this case, the
debugger will be used to troubleshoot tricky bugs and edge cases.
You are encouraged to write tests for your applications rather than
use a debugger to help write new code. You learn about testing and
how this can help development in Hour 10, “Testing Node.js
Applications.” There are times, though, when you are writing code
that you need to examine data or what a function has returned, and
it is often quicker to use a debugger to view this than write a test and
make it pass. Use your judgment about when to write tests and when
to use a debugger, but always test if you can.
SUMMARY
In this hour, you learned about debugging and some of the tools you
can use to debug applications in Node.js. You learned about the
STDIO module, a quick and easy way to add debugging to your
scripts. You then saw how you can gain access to the V8 debugger
and use breakpoints to stop the execution of your code and check the
value of variables. Finally, you were introduced to Node Inspector, a
third-party module that provides access to the WebKit JavaScript
debugger.
Q&A
Q. Which debugger should I use and when?
A. Generally, if your problem seems small and easy to investigate,
using the STDIO module is the quickest way to resolve issues. Many
developers love Node Inspector for the interactive view that it gives
into code as it is executing and only use Node Inspector. Debugging
is a personal thing for developers, so try all the tools and try to
understand their strengths and weaknesses. Debugging is something
you will do a lot, so it is worth investing some time in understanding
your options.
Q. When should I rely on the debugger and when should I write
tests?
A. There is no right or wrong way to develop Node.js applications,
but building up a test suite for your application will make it more
stable in the long term. If you are creating new software, you are
encouraged to write tests and then make them pass. You learn more
about this in Hour 10. Debugging tools should be used to investigate
issues, and if possible, a test should be written to validate that the
bug has been squashed.
Q. How should I track bugs?
A. Many bug trackers are available. GitHub Issues is a popular
choice for many developers because it integrates with source control
if you use Git and provides an easy web-based interface for
managing bugs and issues.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. Why is a bug called a bug in software development?
2. How do you refer to a bug that is no longer a problem?
3. What is the best type of bug?
Quiz Answers
1. Many people think that the term “bug” was invented by Grace
Hopper, a pioneer of computer science. While investigating a
problem on a computer prototype, she is said to have discovered a
moth trapped in a relay, coining the term bug.
2. A bug that is no longer a problem is said to be “fixed,” “closed,” or
“squashed.” Many issue trackers provide support for marking the
status of bugs and calculating how many open bugs there are.
3. There is only one good type of bug in computing: A dead bug!
EXERCISES
1. If you have developed any Node.js applications, try using all three
of the debugging tools described in this hour to examine your
application. Think about the strengths and weaknesses of each
debugging tool. Which tool do you prefer and why?
2. Using the Hello World server on the Node.js home page, start the
server with –debug-brk and then start Node Inspector. Explore the
JavaScript debugger and set some breakpoints. Explore the files list
and see which files are included. Set some breakpoints and try to
understand a little of how the script works and how Node Inspector
gives you access to many details on how the script is working.
3. Explore the Github Issues page on the Node.js repository
at https://github.com/joyent/node/issues. Examine how bug and
issue reports are sent in. Try to understand that some reports are
good with examples while others may be light on information and
difficult for developers to investigate. It may help you file better bug
reports in the future!
Hour 10. Testing Node.js Applications
"8" == 8 // true
'' == '0' // false
0 == '' // true
The second comparison operator is ===. This checks that the values
are the same value and the same type. So, if you try to compare a
string with a number, it will return false. Many experienced
JavaScript developers recommend only using this comparison
operator:
You can then use the module to compare values using the assertion
methods that the assert module provides.
The assert.strictEqual() method allows you to compare two values
using JavaScripts === comparison operator, and it is recommended
that you use this:
assert.strictEqual("hello", "hello");
To run the test, you run the file as you would run any normal node
program. So, if you saved your file as test.js, you can run the test as
follows:
node test.js
If the test passes, you see no output in the terminal. The assert
module is deliberately silent unless there is a failure.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour10/example01.
Follow these steps to compare values using the assert module:
1. Create a file called test.js and copy the following code into it:
var assert = require('assert');
assert.strictEqual("hello", "hello");
2. Run the script:
node test.js
3. You should not see any output in your terminal, indicating that the test
passed.
If you have a test that fails, though, you see that an exception is
thrown. In the following example, the two values are not the same:
assert.strictEqual("hello", "there");
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
AssertionError: "hello" == "there"
By default, AssertionError shows the comparison that failed, but you
can also pass a third argument that is shown if an exception is
thrown:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
AssertionError: Message to show if an exception is thrown
You might use this to log other values in your script that affect the
assertion values.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour10/example02.
Follow these steps to demonstrate an error being thrown:
1. Create a file called test.js and copy the following code into it:
var assert = require('assert');
assert.strictEqual("hello", "there");
2. Run the script:
node test.js
3. You should not see that an exception is thrown in your terminal.
To avoid any danger of being tripped up by JavaScript’s comparison
idiosyncrasies, here are the earlier examples within Node’s assert
module using assert.equal() and assert.strictEqual(). It is
recommended that, to avoid problems, you
use assert.strictEqual() by default.
assert.equal("8", 8) // true
assert.equal('', '0') // false
assert.equal(0, '') // true
assert.strictEqual("8", 8) // false
assert.strictEqual('', '0') // false
assert.strictEqual(0, '') // false
You can also install Nodeunit using the package.json file. For testing
modules, these are usually declared
within devDependencies (see Listing 10.1).
Listing 10.1. A package.json File with Nodeunit
{
"name": "nodeunit_example",
"version": "0.0.0",
"private": true,
"devDependencies": {
"nodeunit": "0.7.4"
}
}
Assertions in Nodeunit map closely to the native assert module and
tests are declared as shown in Listing 10.2.
Listing 10.2. Assertions with Nodeunit
exports.firstTest = function(test){
test.expect(1);
test.strictEqual("hello", "hello");
test.done();
};
exports.secondTest = function(test){
test.expect(1);
test.strictEqual("hello", "there");
test.done();
};
Each test is declared as exports.testName, where testName is a
description of the test. At the beginning of the test, test.expect (n) is
declared where n is the number of assertions you are expecting. This
is used to avoid false test passes. After you finish your test, be sure to
call test.done() to indicate that the test has finished.
If you have called your file test.js and installed Nodeunit globally,
tests can be run with the following command:
nodeunit test.js
./node_modules/nodeunit/bin/nodeunit test.js
For the preceding example, you see the following output (see Figure
10.1):
test.js
✓ firstTest
secondTest
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour10/example03.
Here’s how to test with Nodeunit:
1. Copy the code in Listing 10.1 into a file on your system called package.json.
2. Copy the code in Listing 10.2 into a file on your system called test.js.
3. Install dependencies by running
npm install
4. Run the tests:
./node_modules/nodeunit/bin/nodeunit test.js
5. You should see one test pass and the other fail, along with a stack trace of
the failure.
Nodeunit also supports testing asynchronous code. In the following
example, Node’s fs module is used to read a file from the file system
and assumes that there is a file called test.txt with some text in the
same folder as the test. When the callback is invoked, two tests are
run to test whether an error has been thrown and whether the file
size is not 0. Note that the tests are within the callback function.
Listing 10.3. Asynchronous Tests with Nodeunit
var fs = require('fs');
exports.asyncTest = function(test){
fs.stat('test.txt', function(err, stats) {
test.expect(2);
test.strictEqual(err, null);
test.notStrictEqual(stats.size, 0);
test.done();
})
};
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour10/example04.
Follow these steps to test callbacks with Nodeunit:
1. Copy the code in Listing 10.1 into a file on your system called package.json.
2. Copy the code in Listing 10.3 into a file on your system called test.js.
3. Install dependencies by running
npm install
4. Create a test file to be used in the test called test.txt and add the
following content:
This is a test file
5. Run the test:
./node_modules/nodeunit/bin/nodeunit test.js
6. You should see the test pass.
{
"name": "vows_example",
"version": "0.0.0",
"devDependencies": {
"vows": "0.6.2"
}
}
Vows is also created on top of the assert module and adds BDD style
testing. The synchronous testing example we have been using in this
hour looks like this in Vows (see Listing 10.5).
Listing 10.5. Assertions with Vows
vows.describe('Comparing strings').addBatch({
'when comparing the same strings': {
topic: "hello",
'they should be equal': function (topic) {
assert.strictEqual (topic, "hello");
}
},
'when comparing different strings': {
topic: "hello",
'they should not be equal': function (topic) {
assert.notStrictEqual (topic, "there");
}
}
}).run();
Note the way that Vows encourages you to include a lot of plain
English in your tests. This is not only to help you to think about your
application from the outside in, but also to help with test reporting
and failures. Vows describes these plain English features as follows:
• Description—A description for your test suite
• Context—The context that you are running your test in
• Topic—What you are testing
• Vow—What you expect to happen in the test
Here is the same test annotated with these terms (see Listing 10.6).
Listing 10.6. Annotated Assertions Using Vows
node test.js
If tests pass, the output lists the number of vows that were honored.
This is equivalent to tests passing:
·· ✓ OK » 2 honored
If a test fails, the report includes the context, vow, and the first line
of an exception (see Figure 10.2). This can be useful when
debugging.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour10/example05.
Follow these steps to test with Vows:
1. Copy the code in Listing 10.4 into a file on your system called
package.json.
2. Copy the code in Listing 10.5 into a file on your system called test.js.
3. Install dependencies by running
npm install
4. Run the test:
node test.js
5. You should see the tests pass:
·· ✓ OK » 2 honored
For testing asynchronous code, the topic in vows becomes the
asynchronous function that tests are to be performed on.
Listing 10.7. Asynchronous Tests with Vows
vows.describe('Async testing').addBatch({
'When using fs.stat on a file': {
topic: function () {
fs.stat('test.txt', this.callback);
},
'it should be present': function (err, stat) {
assert.strictEqual(err, null);
},
'it should not be empty': function (err, stat) {
assert. notStrictEqual(stat.size, 0);
}
},
}).run();
Note that the callback is the special this.callback function. This is
available inside all vows topics, and this allows the results of the
callback to be passed to the test functions.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour10/example06.
Follow these steps to test asynchronous code with vows:
1. Copy the code in Listing 10.4 into a file on your system called
package.json.
2. Copy the code in Listing 10.7 into a file on your system called test.js.
3. Create a test file to be used in the test called test.txt and add the following
content:
This is a test file
4. Install dependencies by running
npm install
5. Run the test:
node test.js
6. You should see the tests pass:
·· ✓ OK » 2 honored
Mocha
Mocha is another third-party module for testing Node.js
applications with a modular approach to testing. You can use a range
of assertion libraries including, Node’s assert and a number of third-
party modules. A large range of reporting formats are also
supported, and Mocha is designed to give as much choice to
developers as possible.
You can install Mocha by adding it to the devDependencies section of
the package.json file for your project and then installing with npm
install.
"devDependencies": {
"mocha": "0.10.1"
}
{
"name": "mocha_example",
"version": "0.0.0",
"devDependencies": {
"mocha": "0.10.1"
},
"scripts" : {
"test" : "./node_modules/.bin/mocha test.js"
}
}
When the package.json file has been updated, tests can be run from
the terminal with the following command:
npm test
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour10/example07.
Follow these steps to test with Mocha:
1. Copy the code in Listing 10.8 into a file on your system called
package.json.
2. Copy the code in Listing 10.9 into a file on your system called test.js.
3. Install the dependencies:
npm install
4. Run the test:
npm test
5. You should see the tests pass:
./node_modules/.bin/mocha test.js
..
✓ 2 tests complete
The plain English descriptions are also used in the output from
Mocha if a test fails. This can be useful when debugging to quickly
see the context that the test failed in.
1 of 2 tests failed:
✓ 1 tests complete
SUMMARY
In this hour, you were introduced to testing in Node.js. You saw how
to use the assert module to run simple tests against your code. You
were then introduced to some third-party testing tools that support
both Test Driven Development and Behavior Driven Development.
You were introduced to Nodeunit, Vows, and Mocha. For each of
these modules, you also learned how to run asynchronous code. You
now have a good introduction to test, so there are no excuses!
Q&A
Q. Do I really need to test my code?
A. Yes! Testing has a huge number of benefits. It improves the
stability of the code, makes it easier for other developers to become
involved in your project, and allows you to confidently refactor and
add to your codebase. Do it!
Q. What should I test?
A. You should test code that you write that isn’t related to Node’s
core or third-party modules. As these are generally already covered
by their own tests, there is little point in retesting this code. As you
develop more, you should constantly be thinking, “Do I need to test
this?” If there is any doubt in your mind, write a test for it.
Q. Which of the testing modules should I use?
A. There are many testing libraries in the Node.js ecosystem, so it
can be confusing to know which one to choose. You should certainly
gain a good understanding of Node’s assert module. It may be that
you will feel this can fulfill many of your requirements, and many
third-party testing tools rely on it. Beyond that, which library you
choose will be down to the one that you like the most and which one
matches your coding style.
Q. Can I use these libraries to test client-side code?
A. Yes. Using the browserify module
(https://github.com/substack/node-browserify), you can use the
assert module in the browser. At the time of writing, Nodeunit and
Mocha can both be used in the browser.
Q. I’d like to learn more about testing. Where should I start?
A. Simply writing tests for your application and understand testing
libraries is a great place to start. A good book on the topic is Test-
Driven JavaScript Development by Christian Johansen. Although it
is not Node.js specific, it contains relevant information for
developing Node.js applications. For more information on that book,
visit http://tddjs.com/.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. Why should you use JavaScript’s === equality operator over ==?
2. Why have third-party developers created testing modules in
addition to the assert module?
3. What are some of the advantages of writing tests for your code?
Quiz Answers
1. JavaScript has two equality operators. The first, ==, performs type
coercion and can lead to unexpected results. The second, ===, checks
that values are the same value and the same type. It is generally
recommended to use the triple equals equality operator.
2. The assert module provides a basic testing framework for Node.js.
It can be improved in some areas, however, including test reporting,
testing asynchronous code, and using a BDD or TDD style.
3. Writing tests allows you to be sure that the code works the way
you are expecting. It also allows other developers to understand how
code should work and provides a set of repeatable tests that can be
run when you change or refactor your code.
EXERCISES
1. Read through the documentation for Node’s assert module
at http://nodejs.org/docs/latest/api/assert.html. You can write a lot
of good tests with this module! Take some time to understand the
difference between JavaScript’s comparison methods and how this
relates to the assert module.
2. Extend example01 to add some more tests that allow you to
use assert.equal() and assert.strictEqual().
Compare '0' and 'false' and 'false' and 'null' to understand how
JavaScript treats these.
3. Explore the test folder for Node.js on Github and how the assert
module is used to test itself
at https://github.com/joyent/node/blob/master/test/simple/test-
assert.js. Do not worry if you do not understand all the tests, but try
and understand at least a few.
Hour 11. Deploying Node.js Applications
HEROKU
Heroku was one of the first Platform as a Service (PaaS) providers to
gain popularity among developers. The service is designed around a
Git-based workflow, so if you are familiar with Git for version
control, then deployment is simple. The service was originally
designed to host Ruby applications, but Heroku has since added
support for Node.js, Clojure, Java, Python, and Scala. For the basic
service, Heroku is free.
Signing Up for Heroku
To use the Heroku service, you must first sign up for an account. To
do this, follow these steps:
1. Visit https://api.heroku.com/signup, where you are asked to enter
an email address (see Figure 11.2).
Figure 11.2. Signing up for Heroku
app.listen(3000);
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour11/example02.
Here’s how to prepare a Node.js application for deployment to Heroku:
1. Make a copy of the Express application provided in the code examples at
hour11/example01.
2. At the top of the app.js file, add the following line:
var port = (process.env.PORT || 3000);
3. Remove the following line from app.js:
app.listen(3000);
4. Replace it with the following line:
app.listen(port);
5. In the root of the application, add a file called Procfile and add the
following content:
web: node app.js
6. Install the dependencies with the following:
npm install
7. Start the application to check that it runs OK:
node app.js
Deploying Your Application to Heroku
You are now ready to deploy your application! If you are using your
own application rather than the example Express application
supplied, note that you need to use a package.json file to declare
your dependencies, as Heroku uses this too.
To use Heroku, you must use Git, a version-control tool. Git was
installed by the Heroku installer, so if you installed that, you already
have it on your system! Many developers in the Node.js community
use Git for managing source code.
Heroku recommends that you do not add your node_modules folder
to Git, so this can be achieved by creating a .gitignore file with the
following content:
node_modules
Now, you can create a Git repository and add your source code by
running the following commands from the root of your application:
git init
git add .
git commit -m 'initial commit'
This creates the site for you and automatically sets up everything you
need to deploy. You will see some output like this:
After that, you should be able to visit the URL that was generated in
the Heroku create step and see your site!
Try It Yourself
To deploy a Node.js application to Heroku, follow these steps:
1. Return to your example Express application.
2. Create a Git repository with the following commands:
git init
git add .
git commit -m 'initial commit'
3. Create the application on Heroku with the following command. Note the
URL that was returned from this command:
heroku create --stack cedar
4. Publish the site to Heroku with the following command:
git push heroku master
5. Visit the URL that you noted earlier. You should see your site deployed!
If you need to update your site in the future, commit the new files to
Git and then push to Heroku:
CLOUD FOUNDRY
Cloud Foundry is another PaaS provider for Node.js. One major
difference between Cloud Foundry and other services is that it does
not rely on Git, so if you are not keen on using Git as part of your
deployment process, this may be a good choice for you.
Signing Up for Cloud Foundry
To register for the service,
visit https://my.cloudfoundry.com/signup and enter your details
(see Figure 11.3). You then receive a confirmation link that invites
you to select a password.
Figure 11.3. Signing up for Cloud Foundry
Cloud Foundry requires that you have vmc, a command-line tool for
interacting with the service, installed on your system. This requires
Ruby and RubyGems to be installed on your system. For many, this
may be a turn-off from using this service, but Cloud Foundry does
provide comprehensive documentation on getting everything
installed at http://start.cloudfoundry.com/tools/vmc/installing-
vmc.html.
Did You Know?: Cloud Foundry Is Open Source
The Cloud Foundry platform is open Source and can be used to create your
own private cloud. This means you could run an instance of the Cloud
Foundry software on your own servers if you wanted to. By using Cloud
Foundry’s own servers, though, you don’t have to run your own server.
Try It Yourself
To create a Cloud Foundry account, follow these steps:
1. Sign up for a Cloud Foundry account
at https://my.cloudfoundry.com/signup.
2. Follow the link in your email and add a password.
3. Install vmc and related software following the instructions
at http://start.cloudfoundry.com/tools/vmc/installing-vmc.html.
Preparing Your Application for Cloud Foundry
To prepare your application for Cloud Foundry, you need to make
some amends to the app.js file. If you completed the Heroku
example, you should work on a fresh copy of the example application
that you can find in the code examples at hour11/example01.
Like Heroku, Cloud Foundry assigns the port number dynamically,
so the app.js file needs to be updated to reflect that. At the top of the
app.js file, the following is added:
app.listen(3000);
Is amended to read
app.listen(port);
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour11/example03.
Here’s how to prepare a Node.js application for deployment to Cloud
Foundry:
1. Make a copy of the Express application provided in the code examples at
hour11/example01.
2. At the top of the app.js file, add the following line:
var port = (process.env.VMC_APP_PORT || 3000);
3. Remove the following line from app.js:
app.listen(3000);
4. Replace it with the following line:
app.listen(port);
5. Install the dependencies with the following:
npm install
6. Start the application to check that it runs OK:
node app.js
Deploying Your Application to Cloud Foundry
To deploy the application, the vmc tool is used. This is a command-
line tool for deploying sites to Cloud Foundry. First, the target is set
to the Cloud Foundry servers:
vmc login
This prompts you for a username and password and lets you know if
it has been successful:
Cloud Foundry does not support installing node modules via npm,
so you must ensure that these are installed before you publish your
site. If you haven’t already done so, run the following:
npm install
rm -r node_modules/.bin/
To deploy the site from the root directory for your project, run the
following:
vmc push
This prompts you for a number of questions and then informs you if
the app was successfully deployed:
Try It Yourself
Here’s how to deploy a Node.js Application to Cloud Foundry:
1. Return to your example Express application.
2. Set the target for vmc to the Cloud Foundry servers:
vmc target api.cloudfoundry.com
3. Enter your login credentials:
vmc login
4. Ensure that all dependencies are installed from npm:
npm install
5. Apply the fix for the bug in vmc:
rm -r node_modules/.bin/
6. Deploy the site:
vmc push
7. Visit the URL shown in the output. You should see your site deployed!
NODESTER
Nodester is another PaaS provider for Node.js. It is also open source
software. The service is currently completely free! Nodester claims
that you can deploy a Node.js application in 1 minute!
Signing Up for Nodester
To use the Nodester service, you must first sign up for an account.
To do this, follow these steps:
1. Request a Registration Coupon by
visiting http://nodester.com/ and entering your email address
(see Figure 11.4). Registration coupons are sent out periodically by
the maintainers of Nodester, so you may have to wait a few days to
receive your coupon.
Figure 11.4. Requesting a token from Nodester
2. After you receive your coupon, you can create an account. First,
you need to install the nodester command-line tool from npm:
npm install nodester-cli –g
3. Nodester makes use of cURL to send requests in. For many
developers, this may be too technical, but if you are familiar with
cURL, you can create your account with the following cURL request:
curl -X POST -d
"coupon=yourcoupon&user=youruser&password=123&email=your
@email.com&rsakey=ssh-rsa
AAAA..." http://nodester.com/user
4. If you don’t have cURL installed on your system or you don’t want
to use cURL, you can also enter this information via a form
at http://nodester.com/help.html. One piece of information that you
need to enter is your RSA key. If you are on a UNIX-type (OSX or
Linux) system, you can generate this with the following command:
ssh-keygen -t rsa -C "[email protected]"
5. If you are on Windows, you can install Git for Windows from the
installer at: http://code.google.com/p/msysgit/downloads/list. A
full walkthrough on installing this software is available
at http://help.github.com/win-set-up-git/. Once you have this
installed, you can run
ssh-keygen -t rsa -C "[email protected]"
6. Once you have generated your RSA key using either method, it
will output where the file has been saved. To use Nodester, you need
to open the file id_rsa.pub and copy the contents. You can either add
the contents to the cURL command noted in step 3 or paste it into
the form at http://nodester.com/help.html.
7. Once you have an account, you can set up your local machine for
deploying applications to Nodester. Note
that <username> and <password> should be replaced with your own
credentials:
nodester user <username> <password>
nodester info verifying credentials
nodester info user verified..
nodester info writing user data to config
Try It Yourself
To sign up for a Nodester account, follow these steps:
1. Request a Nodester coupon from http://nodester.com.
2. Once you receive the coupon, generate an RSA key if you need to:
ssh-keygen -t rsa -C "[email protected]"
3. Complete the registration process either with a cURL request or by
completing the form at http://nodester.com/help.html:
curl -X POST -d
"coupon=yourcoupon&user=youruser&password=123&[email protected]
om&rsakey=ssh-rsa AAAA..." http://nodester.com/user
4. Ensure you have installed the Nodester CLI on your local machine:
npm install nodester-cli –g
5. Set up your local machine for Nodester:
nodester user <username> <password>
Preparing Your Application for Nodester
To create a new application on Nodester, run the following
command:
app.listen(3000);
app.listen(port);
Deploying Your Application to Nodester
To install the dependencies on Nodester from the “yourapp” folder,
run the following:
git add .
git commit -m 'adding site files'
git push origin master
var io = require('socket.io').listen(server);
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://127.0.0.1:3000');
</script>
This includes a single JavaScript file that allows the client (or
browser) to communicate with the server. Note this can be any
server, but in this case, it is connected to the Node.js server running
at 127.0.0.1 on port 3000.
Did You Know?: Socket.IO Serves the Client-Side Library Automatically
You may have noticed that in the client-side code, there is a JavaScript file
socket.io.js. If you are running your server and client on the same server, the
Socket.IO library will serve this automatically for you. This contains all the
necessary code for the browser to connect to the server and send and receive
messages.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour12/example01.
Follow these steps to connect a Socket.IO server and client:
1. Create a new folder called socket.io.
2. Within the socket.io folder, create a new file called package.json and add
the following content to declare Socket.IO as a dependency:
{
"name":"socketio_example",
"version":"0.0.1",
"private":true,
"dependencies":{
"socket.io":"0.8.7"
}
}
3. Within the socket.io folder, create a new file called app.js with the
following content:
var http = require('http');
var fs = require('fs');
var io = require('socket.io').listen(server);
var counter = 0;
Now that the server has a count of the number of clients on the
server, the clients themselves can be updated using Socket.IO to
send the data to all clients using socket.emit.broadcast. In this
example, this data is sent under the 'users' event, since it relates to
the number of users:
This works well when a client disconnects, but when a new client
connects, they would not receive an update,
because socket.emit.broadcast does not go to the connecting client. To
resolve this, an additional socket.emit message is sent as the
same 'users' event to the connecting client:
The Socket.IO part of the Node.js server now looks like Listing 12.3.
Listing 12.3. A Real-Time Counter with Socket.IO
var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket) {
count++;
socket.emit('users', { number: count });
socket.broadcast.emit('users', { number: count });
socket.on('disconnect', function () {
count--;
socket.broadcast.emit('users', { number: count });
});
});
The server sends the number of connected users to clients! But, how
do we show this to the users? Some client-side JavaScript needs to
be added to listen for the 'users' event to receive and display the
data. This can be achieved with a small amount of Socket.IO code
and some JavaScript to update the DOM (see Listing 12.4).
Listing 12.4. Handling Events on the Client
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://127.0.0.1:3000');
var count = document.getElementById('count');
socket.on('users', function (data) {
count.innerHTML = data.number
});
</script>
When a 'users' event is received, the number received is used to
update a paragraph with an id of count within the HTML page.
Conveniently, the syntax for receiving and sending message on the
client side and server side is the same.
To make the number appear correctly, a paragraph tag with an id
of count must be added to the HTML:
<p id="count"></p>
var io = require('socket.io').listen(server);
</body>
</html>
5. Install the dependencies by running the following from a terminal:
npm install
6. Start the server by running the following from a terminal:
node app.js
7. Open a browser at http://127.0.0.1:3000.
8. You should see a page showing “Socket.IO Example” and that one user is
on the server.
9. Open another browser tab at http://127.0.0.1:3000.
10. You should see that two users are now on the server. Switch to the other
tab, and you should see that this has also been updated to two users
(see Figure 12.1).
11. Close one of the browser tabs you have open. You should see the number
of users on the server drop to 1.
You also saw that you can send messages from a server to single
clients:
The code for sending messages from clients to the server is exactly
the same! The difference from the real-time counter example is that
the message initiates from the client side and from client-side
JavaScript. So, on the client side (or browser), there must be a way
for users to add a message and submit it to the server. One way this
can be achieved is to create a form and then use some JavaScript to
capture whatever a user puts into the form and send it to the server.
In this example, jQuery is used, but if you prefer to write this in
vanilla JavaScript, you could do so. The form to capture a message is
a standard HTML form:
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://127.0.0.1:3000');
var message = document.getElementById('message');
$(message.form).submit(function() {
socket.emit('message', { text: message.value });
return false;
});
socket.on('broadcast', function (data) {
$('form').after('<p>' + data.text + '</p>');
});
</script>
var io = require('socket.io').listen(server);
BI-DIRECTIONAL DATA
In the real-time counter and messaging server examples, you have
seen how to send data from the server to clients and from the clients
to the server. You have sent data bi-directionally (both ways), and
this is a key feature of Socket.IO and WebSockets that you should
understand. What WebSockets enables is a way for clients and
servers to push data to each other whenever they want to. This really
changes the landscape of the Web and the types of applications that
you can create for the browser. At the same time, as a browser is
sending a message to the server, the server might be sending the
client a message, too. Other clients might be updating the server,
and clients may be joining and leaving the server. You can see that
even in a small example, many events are happening! JavaScript is a
perfect fit for this. It is an event-driven language, and, given that
Node.js brings server-side networking to the party, you can see how
JavaScript is set to become the darling of these types of application.
To cement the theory of sending data both ways, the final example in
this hour demonstrates the flow of data between client and server
and how events can be used to respond with more messages. In
computing, the term PING is a request to a service or server to tell
the requestor that it is up or alive. The expected response is PONG.
If you think of a game of table tennis, the ball is hit over the net and
then it is hit back—ping-pong! You use this idea to explore message
flow in Socket.IO by sending a message requesting a PONG response
from the other end.
On the server side, the example
1. Sends a PONG response when it receives a PING.
2. Logs a message to the terminal when it receives a PONG
response.
3. Sends a PING message to clients every 10 seconds.
On the client side, the example
1. Sends a PONG response when it receives a PING.
2. Logs a message to the console when it receives a PONG response.
3. Sends a PING message to the server when a user clicks a Submit
button.
Before you get into the code for this functionality, it is important to
understand that these events can happen at any time and in any
order. The messages are bi-directional, after all! To achieve the
server-side functionality, the Socket.IO code looks like Listing 12.5.
Listing 12.5. Demonstrating Messaging in Socket.IO
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://127.0.0.1:3000');
socket.on('ping', function (data) {
console.log('Received PING. Sending PONG..');
socket.emit('pong', { text: 'PONG' });
});
socket.on('pong', function (data) {
console.log('Received PONG response. PONG!');
});
$('#ping').click(function() {
console.log('Sending PING to server..')
socket.emit('ping', { text: 'PING' });
});
</script>
var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket) {
socket.on('ping', function (data) {
console.log('Received PING. Sending PONG..');
socket.emit('pong', { text: 'PONG' });
});
socket.on('pong', function (data) {
console.log('Received PONG response. PONG!');
});
setInterval(function() {
console.log('Sending PING to client..');
socket.emit('ping', { text: 'PING' });
}, 10000);
});
4. Within the ping_pong folder, create a new file called index.html with the
following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Socket.IO Example</title>
</head>
<body>
<h1>Socket.IO Example</h1>
<button id="ping">Send PING to server</button>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></s
cript>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://127.0.0.1:3000');
socket.on('ping', function (data) {
console.log('Received PING. Sending PONG..');
socket.emit('pong', { text: 'PONG' });
});
socket.on('pong', function (data) {
console.log('Received PONG response. PONG!');
});
$('#ping').click(function() {
console.log('Sending PING to server..')
socket.emit('ping', { text: 'PING' });
});
</script>
</body>
</html>
5. Install the dependencies by running the following from a terminal:
npm install
6. Start the server by running the following from a terminal:
node app.js
7. Open Firefox, Chrome, or Safari at http://127.0.0.1:3000 and open a
console window.
8. Watch the browser console as 'ping' events are received from the server
and 'pong'responses are sent (see Figure 12.3).
9. In the browser window, click the Submit button to send a 'ping' to the
server.
10. Watch the terminal log to see that a 'ping' has been received and
a 'pong' has been returned (see Figure 12.3).
SUMMARY
In this hour, you saw one of the things that really makes Node.js
different. You saw how using Socket.IO can create applications that
allow communications between the server and clients in real-time!
You learned how to send data from the server to the client, and then
from the client to the server. You understood how to receive data on
both the server and the client and how to use client-side JavaScript
to display the data to users. In the final example, you understood
how messages can trigger other messages and how the flow of
messages can be bi-directional. You learned much of the theory
behind Socket.IO in this hour, and you have all the tools you need to
build the real-time web!
Q&A
Q. Are WebSockets supported in all browsers?
A. The short answer is no. At the time of writing, the WebSockets
standard is still in Editor’s Draft, so the standard is not finished.
Many browser vendors have already implemented WebSockets,
although some have disabled the feature due to security concerns.
Although it is expected that support for WebSockets will become
standard across all browsers, support for WebSockets cannot be
expected at the time of writing. The good news is that Socket.IO
accounts for WebSockets support not being available in all browsers.
If WebSockets support is available, Socket.IO will choose that. If it is
not, Socket.IO will try to use Adobe Flash Sockets, Ajax long polling,
Ajax multipart streaming, Forever iFrame, or JSONP polling. You do
not need to understand what these are as Socket.IO handles these
seamlessly, although you can choose which transports to enable if
you need to. This means that if you use Socket.IO, you can expect
support from a broad range of browsers. At the time of writing,
Socket.IO browser support is listed as follows: Internet Explorer
5.5+, Safari 3+, Google Chrome 4+, Firefox 3+, Opera 10.61+,
iPhone Safari, iPad Safari, Android WebKit, and WebOs WebKit.
Pretty impressive!
Q. Can I connect across different domains?
A. Yes, you can. If you have experience using Ajax, you may know
that to enable cross-domain requests, you must use additional
techniques. You do not need these when using Socket.IO. It just
works!
Q. What is the difference between Ajax and Socket.IO?
A. Socket.IO provides a great deal more functionality than Ajax,
namely the ability to easily push data out to clients in real-time. If
WebSockets are available, then the connection between the client
and server will be persistent. With Ajax, a connection needs to be
established each time.
Q. Should I use WebSockets or Ajax?
A. WebSockets and Ajax serve different purposes, so it is difficult to
say whether to use one or the other. Ajax is good for scenarios where
data is requested by a browser every now and again and potentially
needs to be cached. If you frequently need to send and receive data
between the client and you have many clients, then WebSockets
should be used. If you are building a networked application where
clients can communicate with each other, WebSockets are a better
choice.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. What is the difference between push and pull messaging?
2. How well are WebSockets currently supported in browsers?
(Hint: You can use http://caniuse.com/ to help you.)
3. What is the advantage of using Socket.IO over plain WebSockets?
Quiz Answers
1. Push messaging means that new messages are pushed out to
clients through a persistent connection. This means that a client
does not have to make a request for new messages. By contrast, pull
messaging means that the client has to check the server to see
whether any new messages are available.
2. If you visit http://caniuse.com and type WebSockets into the
Search box, you will see how well WebSockets are supported.
Particularly on mobile devices, support for WebSockets can be
patchy, adding more weight to the argument for using Socket.IO.
3. Socket.IO falls back to other transport methods if WebSockets are
not available. This means that you can support more browsers in
your application without writing any more code.
EXERCISES
1. Create a Socket.IO application that sends clients the message “You
have connected!” when a new client connects.
2. Extend Exercise 1 to display the message to clients by writing
some client-side JavaScript to receive the message.
3. Explore the client-side JavaScript debuggers available in Firefox,
Safari, and Chrome. If you need to refresh your memory, these were
covered in Hour 9, “Debugging Node.js Applications.” If you do not
have access to all of these browsers, just choose the ones that you
have available on your system. Work through some of the examples
in this hour and understand how you can use the debugger and
console.log on the client-side to help develop Socket.IO applications.
Hour 13. A Socket.IO Chat Server
app.listen(3000);
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
socket.on('welcome', function (data) {
console.log(data.text);
});
</script>
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour13/example01.
Follow these steps to create a basic Socket.IO server using Express:
1. Create a new folder called socket.io_express.
2. Within the socket.io_express folder, create a new file called package.json
and add the following content to declare Socket.IO and Express as a
dependencies:
{
"name":"socket.io-express-example",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.4",
"socket.io":"0.8.7"
}
}
3. Within the socket.io_express folder, create a new file called app.js with
the following content:
var app = module.exports = express.createServer(),
io = require('socket.io').listen(app);
app.listen(3000);
<form id="set-nickname">
<label for="nickname">Nickname:</label>
<input type="text" id="nickname" />
<input type="submit" />
</form>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
jQuery(function ($) {
var nickname = $('#nickname');
var setNicknameForm = $('#set-nickname');
setNicknameForm.submit(function(event) {
event.preventDefault();
socket.emit('nickname', nickname.val());
});
});
</script>
This adds a listener for the nickname event and receives the data so
that it can be used.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour13/example02.
To receive data from the client with Socket.IO, follow these steps:
1. Create a new folder called sending_nickname.
2. Within the sending_nickname folder, create a new file called package.json
and add the following content to declare Socket.IO and Express as
dependencies:
{
"name":"socket.io-express-example",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.4",
"socket.io":"0.8.7"
}
}
3. Within the sending_nickname folder, create a new file called app.js with
the following content:
var app = module.exports = express.createServer(),
io = require('socket.io').listen(app);
app.listen(3000);
This finds the item in the array and then removes it. This can be
employed in the Socket.IO server to remove the nickname whenever
a user disconnects. Socket.IO is able to set a variable for each client
to allow the user to access the nickname in the disconnect event.
The nickname event must be amended to set this variable so that it can
be used later:
socket.on('disconnect', function () {
if (!socket.nickname) return;
if (nicknames.indexOf(socket.nickname) > -1) {
nicknames.splice(nicknames.indexOf(socket.nickname), 1);
}
});
app.listen(3000);
This can be amended to take a callback that will receive data from
the server side after the message has been received. This allows the
client side to know how things went at the other end:
jQuery(function ($) {
var nickname = $('#nickname');
var setNicknameForm = $('#set-nickname');
setNicknameForm.submit(function(event) {
event.preventDefault();
socket.emit('nickname', nickname.val(), function (data) {
if (data) {
console.log('Nickname set successfully');
setNicknameForm.hide();
} else {
setNicknameForm.prepend('<p>Sorry - that nickname is
already taken.</p>');
}
});
});
});
The anonymous function inside the nickname event now takes two
arguments: the data received from the client (data) and a function
(callback). The function is the callback function that will be returned
to the client from the server. JavaScript’s indexOf() function can be
used on an array to return whether an item is already in an array. If
the nickname is not in the array, a value of -1 will be returned so true
or false can be set depending on a simple test to see whether the
nickname is in the array. This is returned to the client side to either
hide the form or tell the user that the nickname has been taken.
Callbacks are an extremely powerful tool to use in messaging
between client and server, and you should use them whenever you
need the client side to know what happened to the data that it just
sent.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour13/example04.
Follow these steps to demonstrate callbacks between client and server:
1. Create a new folder called callbacks.
2. Within the callbacks folder, create a new file called package.json and add
the following content to declare Socket.IO and Express as dependencies:
{
"name":"socket.io-express-example",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.4",
"socket.io":"0.8.7"
}
}
3. Within the callbacks folder, create a new file called app.js with the
following content:
var app = require('express').createServer(),
io = require('socket.io').listen(app),
nicknames = [];
app.listen(3000);
11. Open another browser tab, enter another nickname, and click Submit.
12. In the server logs, you should see your nickname in the list of nicknames
that the server currently has. You should see that the form has been hidden
on the client side and a message has been logged to the JavaScript console.
13. Open another browser tab, enter the same nickname, and click Submit.
14. You should see a message that the nickname has been taken (see Figure
13.3), indicating that the server-side validation worked!
Figure 13.3. Using callbacks to validate a nickname
io.sockets.emit('nicknames', nicknames);
On the client side, this can be received and then written to a page to
show the user who is on the server. A simple JavaScript loop can be
used to handle the data, iterating over the array and adding the
nicknames to the DOM. To give the JavaScript somewhere to write
the list of nicknames, an empty section tag is added to the HTML
document with an id of “nicknames” containing and empty
unordered list:
<form id="send-message">
<textarea id="message"></textarea>
<input type="submit" />
</form>
This form will be hidden with display: none in CSS until the
nickname has been successfully set. After the nickname has been set
successfully, the callback can be used to hide the nickname form and
show the message form. The client-side JavaScript for submitting a
nickname now looks like this:
If the callback from the server indicates that the nickname was
successfully added, the form to add a nickname is hidden using
jQuery’s hide() method, and the form to submit messages is shown
using jQuery’s show() method, effectively switching them out. Now
that a form is visible to the user, some client-side JavaScript can
then be added to listen for the submit event on the form and send
the contents of the text area to the Socket.IO server. The form is also
cleared and focus given to the form so that the user can enter
another message if she wants:
Finally, some CSS is needed to hide the message form when the page
initially loads. A few rudimentary styles are also added to display the
list of nicknames on the right of the page, to make the text area box a
little bigger and to add some padding between the nickname and the
message:
<style>
#send-message { display: none; }
#nicknames { width: 300px; float: right; }
#message { width: 300px; height: 100px; }
#messages p strong { margin-right: 5px; }
</style>
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour13/example06.
Follow these steps to message between a server and client:
1. Create a new folder called messaging.
2. Within the messaging folder, create a new file called package.json and add
the following content to declare Socket.IO and Express as a dependencies:
{
"name":"socket.io-express-example",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.4",
"socket.io":"0.8.7"
}
}
3. Within the messaging folder, create a new file called app.js with the
following content:
var app = require('express').createServer(),
io = require('socket.io').listen(app),
nicknames = [];
app.listen(3000);
socket.on('disconnect', function () {
if (!socket.nickname) return;
if (nicknames.indexOf(socket.nickname) > -1) {
nicknames.splice(nicknames.indexOf(socket.nickname), 1);
}
console.log('Nicknames are ' + nicknames);
io.sockets.emit('nicknames', nicknames);
});
});
4. Within the messaging folder, create a new file called index.html with the
following content:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Socket.IO Express Example</title>
<style>
#send-message { display: none; }
#nicknames { width: 300px; float: right; }
#message { width: 300px; height: 100px; }
#messages p strong { margin-right: 5px; }
</style>
</head>
<body>
<h1>Socket.IO Express Example</h1>
<form id="set-nickname">
<label for="nickname">Nickname:</label>
<input type="text" id="nickname" />
<input type="submit" />
</form>
<form id="send-message">
<textarea id="message"></textarea>
<input type="submit" />
</form>
<section id="nicknames">
<ul></ul>
</section>
<section id="messages">
</section>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></s
cript>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
jQuery(function ($) {
var nickname = $('#nickname');
var setNicknameForm = $('#set-nickname');
var nicknamesList = $('#nicknames ul');
var messageForm = $('#send-message');
var message = $('#message');
var messages = $('#messages');
setNicknameForm.submit(function(event) {
event.preventDefault();
socket.emit('nickname', nickname.val(), function (data) {
if (data) {
console.log('Nickname set successfully');
setNicknameForm.hide();
messageForm.show();
} else {
setNicknameForm.prepend('<p>Sorry - that nickname is already
taken.</p>');
}
});
});
messageForm.submit(function(event) {
event.preventDefault();
socket.emit('user message', message.val());
message.val('').focus();
});
socket.on('nicknames', function (data) {
var html = '';
for (var i = 0; i < data.length; i++) {
html += '<li>' + data[i] + '</li>';
}
nicknamesList.empty().append(html);
});
socket.on('user message', function (data) {
messages.append('<p><strong>' + data.nick + '</strong> '
+ data.message + '</p>');
});
});
</script>
</body>
</html>
SUMMARY
In this hour, you learned how to create a fully featured chat server
with Socket.IO. You learned how to use Socket.IO within Node.js
applications that use the Express framework. You learned how to
manage a list of nicknames on the server and how to use callbacks to
return information on the outcome of sending data to the original
sender. You also saw how to broadcast the list of nicknames and how
to add messaging into the application. Most importantly, you
learned how to use client-side and server-side JavaScript together to
create a rich, real-time application!
Q&A
Q. I’ve noticed that more of the Socket.IO application logic is on the
client side than the server side. Is this bad design?
A. A feature of single-page applications, such as the chat server, you
created is that much of the application logic becomes client-side
JavaScript. This is a paradigm shift from classic applications where
most of the logic lived on the server and was used to send HTML to
the browser. This design can also be known as “thick server, thin
client” because most of the processing happens on the server.
Modern JavaScript applications are flipping this idea around so that
more logic is happening on the browser. This leads to a world of
exciting possibilities, but is not without problems. With a server, you
can be 100% sure of the capabilities of the hardware and software
being used. With browsers, you could be relying on a range of
different JavaScript engines, CSS rendering capabilities, not to
mention screen resolutions. Anyone who has been involved in front-
end development knows of the pain of deploying to different
browsers! Libraries, like Socket.IO and jQuery, abstract many of the
differences between browsers for you. This is another argument for
using libraries! It is not bad design, but it does bring some new
challenges.
Q. Where can I host an application like this?
A. Because Socket.IO depends on WebSockets, you need to find a
host that supports WebSockets. Not all Node.js PaaS hosts support
WebSockets. At the time of writing, Nodester, Nodejitsu,
Nodesocket, Cure, and Cloudnode support WebSockets.
Q. Can you share an Express session with a Socket.IO one?
A. Yes, you can. Many approaches use Redis (a key-value store) to
store the Express session and give Socket.IO access to it. The
implementation is beyond the scope of this hour.
Q. Could you add keyword banning and logging to this application?
A. Absolutely! You can hook into the Socket.IO event listeners to
check messages for a list of banned words or to write the messages to
a file or data store.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. Do you have to use Express to use Socket.IO?
2. What happens to the list of nicknames if the server process is
killed or dies?
3. Can you host your Socket.IO server and web application server in
different places?
Quiz Answers
1. No. Socket.IO is independent from Express. Furthermore,
Socket.IO abstracts WebSockets for you, so if you want to, you can
write your own WebSockets server and handle the client-side code.
2. The list of nicknames is only persisted in memory in the examples
in this hour, so if the process is killed or dies, the list of nicknames
will be lost. You could use a key value store like Redis to hold this list
if you wanted.
3. Yes. Although the Socket.IO server and web application are in the
same process, in these examples, you can split them if you want.
EXERCISES
1. Using the knowledge that you learned in Hour 12, add a new
feature to the application so that a message is posted to the chat
window every time a user joins or leaves the server. Consult the code
examples for the book and hour13/example07 for an approach to do
this.
2. Experiment with adding some styling to the chat application. If
you do not have any design skills, work with a colleague who
understands CSS and HTML to add some simple styling.
3. If you are looking for a challenge, add a new feature of private
messaging to the application. This should function so a message can
be sent between two connected clients rather than being broadcast
to everyone on the server.
Hour 14. A Streaming Twitter Client
4. When the page refreshes, you see that values have been added for
access token and access token secret (see Figure 14.3). Now, you are
ready to start using the API!
{
"name":"socket.io-twitter-example",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.4",
"ntwitter":"0.2.10"
}
}
app.listen(3000);
app.listen(3000);
"user": {
"profile_sidebar_border_color": "eeeeee",
"profile_background_tile": true,
"profile_sidebar_fill_color": "efefef",
"name": "Eoin McMillan ",
"profile_image_url":
"http://a1.twimg.com/profile_images/1380912173/Screen_shot_20
11-06-03_at_7.35.36_PM_normal.png",
"created_at": "Mon May 16 20:07:59 +0000 2011",
"location": "Twitter",
"profile_link_color": "009999",
"follow_request_sent": null,
"is_translator": false,
"id_str": "299862462",
"favourites_count": 0,
"default_profile": false,
"url": "http://www.eoin.me",
"contributors_enabled": false,
"id": 299862462,
"utc_offset": null,
"profile_image_url_https":
"https://si0.twimg.com/profile_images/1380912173/Screen_shot_
2011-06-03_at_7.35.36_PM_normal.png",
"profile_use_background_image": true,
"listed_count": 0,
"followers_count": 9,
"lang": "en",
"profile_text_color": "333333",
"protected": false,
"profile_background_image_url_https":
"https://si0.twimg.com/images/themes/theme14/bg.gif",
"description": "Eoin's photography account. See @mceoin for
tweets.",
"geo_enabled": false,
"verified": false,
"profile_background_color": "131516",
"time_zone": null,
"notifications": null,
"statuses_count": 255,
"friends_count": 0,
"default_profile_image": false,
"profile_background_image_url":
"http://a1.twimg.com/images/themes/theme14/bg.gif",
"screen_name": "imeoin",
"following": null,
"show_all_inline_media": false
}
app.listen(3000);
The streaming API request can now be augmented to push the data
out to any connected Socket.IO clients whenever a new data event is
received:
Instead of logging the data to the console, you are now doing
something useful with the data by pushing it out to connected
clients. A simple JSON structure is created to hold the name of the
user and the tweet. If you want to send more information to the
browser, you could simply extend the JSON object to hold other
attributes.
You may have noticed that, instead of using io.sockets.emit as you
did in Hours 12 and 13, you are now using io.sockets.volatile.emit.
This is an additional method provided by Socket.IO for scenarios
where certain messages can be dropped. This may be down to
network issues or a user being in the middle of a request-response
cycle. This is particularly the case where high volumes of messages
are being sent to clients. By using the volatile method, you can
ensure that your application will not suffer if a certain client does
not receive a message. In other words, it does not matter whether a
client does not receive a message.
The Express server is also instructed to serve a single HTML page so
that the data can be viewed in a browser.
<ul class="tweets"></ul>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
jQuery(function ($) {
var tweetList = $('ul.tweets');
socket.on('tweet', function (data) {
tweetList
.prepend('<li>' + data.user + ': ' + data.text + '</li>');
});
});
</script>
Whenever new data is received from the API, the love counter will be
incremented if the word “love” is found and so on.
JavaScript’s indexOf() string function can be used to look for words
within a tweet and provides a simple way to analyze the content of
tweets:
Because some tweets may contain both “love” and “hate,” the total is
incremented each time a word is found. This means that the total
counter represents the total number of times “love” or “hate” was
mentioned in a tweet rather than the total number of tweets.
Now that the application is maintaining a count of the occurrences
of words, this data can be added to the tweet emitter and pushed to
connected clients in real-time. Some simple calculation is also used
to send the values as a percentage of the total number of tweets:
io.sockets.volatile.emit('tweet', {
user: data.user.screen_name,
text: data.text,
love: (love/total)*100,
hate: (hate/total)*100
});
<ul class="percentage">
<li class="love">0</li>
<li class="hate">0</li>
</ul>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
jQuery(function ($) {
var tweetList = $('ul.tweets'),
loveCounter = $('li.love'),
hateCounter = $('li.hate');
socket.on('tweet', function (data) {
tweetList
.prepend('<li>' + data.user + ': ' + data.text + '</li>');
loveCounter
.text(data.love + '%');
hateCounter
.text(data.hate + '%');
});
});
</script>
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour14/example04.
To analyze data from Twitter’s streaming API, follow these steps:
1. Create a new folder called percentages.
2. Within the percentages folder, create a new file called package.json and
add the following content to declare ntwitter, Express, and Socket.IO as
dependencies:
{
"name":"socket.io-twitter-example",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.4",
"ntwitter":"0.2.10",
"socket.io":"0.8.7"
}
}
3. Within the percentages folder, create a new file called app.js with the
following content. Remember to replace the keys and secrets with your own:
var app = require('express').createServer(),
twitter = require('ntwitter'),
io = require('socket.io').listen(app),
love = 0,
hate = 0,
total = 0;
app.listen(3000);
<ul class="percentage">
<li class="love">
<span>0</span>
</li>
<li class="hate">
<span>0</span>
</li>
</ul>
Some CSS can then be added to the head of the HTML document
that makes the unordered list look like a bar graph. The list items
represent the bars with colors of pink to represent love and black to
represent hate:
<style>
ul.percentage { width: 100% }
ul.percentage li { display: block; width: 0 }
ul.percentage li span { float: right; display: block}
ul.percentage li.love { background: #ff0066; color: #fff}
ul.percentage li.hate { background: #000; color: #fff}
</style>
Finally, some client-side JavaScript allows the bars (the list items) to
be resized dynamically based on the percentage values received from
the server:
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
jQuery(function ($) {
var tweetList = $('ul.tweets'),
loveCounter = $('li.love'),
hateCounter = $('li.hate'),
loveCounterPercentage = $('li.love span'),
hateCounterPercentage = $('li.hate span');
socket.on('tweet', function (data) {
loveCounter
.css("width", data.love + '%');
loveCounterPercentage
.text(Math.round(data.love * 10) / 10 + '%');
hateCounter
.css("width", data.hate + '%');
hateCounterPercentage
.text(Math.round(data.hate * 10) / 10 + '%');
tweetList
.prepend('<li>' + data.user + ': ' + data.text + '</li>');
});
});
</script>
app.listen(3000);
io.sockets.volatile.emit('tweet', {
user: data.user.screen_name,
text: data.text,
love: (love/total)*100,
hate: (hate/total)*100
});
});
});
{
"name": "Darth Vader",
"occupation": "Dark Lord of the Sith",
"home": "Tatooine"
}
{
"name": "Twitter API",
"description": "The Real Twitter API. I tweet about API changes,
service issues
and happily answer questions about Twitter and our API. Don't get
an answer? It's
on my website.",
"time_zone": "Pacific Time (US & Canada)",
"profile_background_image_url":
"http://a3.twimg.com/profile_background_
images/59931895/twitterapi-background-new.png",
"friends_count": 20,
"statuses_count": 2404,
"status": {
"coordinates": null,
"created_at": "Wed Dec 22 20:08:02 +0000 2010",
"id_str": "17672734540570624",
"text": "Twitter downtime - Twitter is currently down. We are
aware of the
problem and working on it. http://t.co/37zl2jI",
"retweet_count": 30,
"in_reply_to_status_id_str": null,
},
"following": true,
"screen_name": "twitterapi"
}
The status key shows how you can nest data and create more
complex structures. Data structures can be created however you
need them to be created, and most developers find that JSON is
much simpler to read than XML.
Watch Out: Keys and Strings Must Be in Double Quotes
To be valid, JSON keys and strings must be in double quotes. If you are using
a module to generate your JSON, this will likely be handled for you. If you
are writing JSON by hand, remember to use double quotes. For more
information, see http://simonwillison.net/2006/oct/11/json/.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour15/example01.
Follow these steps to send JSON with Node.js:
1. Create a file called app.js and copy the following code into it:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end('{"name": "Darth Vader","occupation": "Dark Lord of the
Sith","home": "Tatooine"}');
}).listen(3000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:3000/');
2. Run the script:
node app.js
3. Using Google Chrome, open http://127.0.0.1:3000.
4. Click the HTTP Headers icon (you learned how to install this in Hour 8,
“Persisting Data”) to see that it is JSON.
5. You should see that the Content-Type is correctly set to application/json
(see Figure 15.1).
var obj = {
name : "Officer",
surname : "Dibble"
}
JSON.stringify(obj);
Using this approach, you can then send the JSON as before:
Did You Know?: You Can Use JSON.stringify in Most Browsers, Too
You can also use JSON.stringify to convert JavaScript objects to JSON in
most browsers. It is available from Internet Explorer 7 and has good support
in mobile browsers. For a full matrix of browser support,
see http://caniuse.com/json.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour15/example02.
Follow these steps to convert JavaScript objects to JSON:
1. Create a file called app.js and copy the following code into it:
var http = require('http');
var obj = {
name : "Officer",
surname : "Dibble"
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'application/json'});
res.end(JSON.stringify(obj));
}).listen(3000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:3000/');
2. Run the script:
node app.js
3. Using Google Chrome, open http://127.0.0.1:3000.
4. Click the HTTP Headers icon (you learned how to install this in Hour 8) to
see that it is JSON (see Figure 15.2).
5. You should see the data sent as a JSON string to the browser.
var obj = {
name : "Officer",
surname : "Dibble"
}
console.log('JavaScript object:');
console.log(obj);
var json = JSON.stringify(obj);
console.log('JavaScript object to JSON:');
console.log(json);
console.log('JavaScript object:');
console.log(obj);
var options = {
host: 'search.twitter.com',
port: 80,
path: '/search.json?q=%23node.js'
};
var ingredients = [
{ name: 'eggs' },
{ name: 'flour' },
{ name: 'milk' }
];
If you were to perform a curl request on this route, you would get the
following back:
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 127
Connection: keep-alive
[{"name":"eggs"},{"name":"flour"},{"name":"milk"}]
var rebels = [
{ name: 'Han Solo' },
{ name: 'Luke Skywalker' },
{ name: 'C-3PO' },
{ name: 'R2-D2' },
{ name: 'Chewbacca' },
{ name: 'Princess Leia' }
];
app.listen(3000);
console.log("Express server listening on port %d in %s mode",
app.address().port, app.settings.env);
3. Install the dependencies:
npm install
4. Run the script:
node app.js
5. Using Google Chrome, open http://127.0.0.1:3000.
6. You should see a list of Rebel Alliance members as JSON. Check the
headers using the HTTP Headers extension (see Figure 15.3).
Figure 15.3. Sending JSON with Express
var ingredients = [
{ name: 'eggs' },
{ name: 'flour' },
{ name: 'milk' }
];
This sets the response code and content type, and converts to JSON
just as res.send() does. So, what is the difference between these two
methods?
In Express, res.send is designed to be a high-level response utility
that allows you to pass all manner of objects into it. This could be a
number of things:
• An empty response
• Some JSON
• Some HTML
• Some plain text
• Nothing with a 404 response
In code, these look like this:
res.send();
res.send({ greeting: 'OHAI!' });
res.send('<p>some html</p>');
res.send('text', { 'Content-Type': 'text/plain' }, 201);
res.send(404);
You can see that you can send a lot of different things
using res.send and that it is clever enough to work out what you are
trying to send.
If you choose to use res.json(), you are explicitly saying I want to
send JSON and nothing else. This might be a good option if you
know you only ever want to send JSON or if you need to send a
string as JSON. Using the res.send() method assumes that you are
sending HTML if you give it a string. But using res.json() allows you
to send a string as JSON. If this is confusing, just use res.send(). It
will almost always do what you want!
Building the Application
To understand creating a JSON API with Node.js, you build a simple
API similar to the tasks application created in Hour 8. This allows
you to create, read, update, and delete records. Based on the testing
tools you learned in Hour 10, “Testing Node.js Applications,” you
follow a Behavior Driven Development approach, so writing tests
first and then writing code to make those tests pass. You use
MongoDB as the data store as you did in Hour 8.
Watch Out: Make Sure That MongoDB Is Running
To follow these examples, you need to ensure MongoDB is running on your
machine. If you need a refresher, refer to Hour 11, “Deploying Node.js
Applications.”
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour15/example06.
To create a JSON API with Node.js, follow these steps:
1. Create a new folder called json_api.
2. Within the new folder, create a file called app.js and enter the following
content. This sets up a basic Express application with a Mongoose model for
tasks and development and test environments:
var express = require('express');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
app.configure(function(){
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
mongoose.connect('mongodb://localhost/todo_development');
app.listen(3000);
});
app.configure('test', function() {
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
mongoose.connect('mongodb://localhost/todo_test');
app.listen(3001);
});
In these two tests, we use the assert module from Node.js’s standard
library to first check that the response code is 200 and that JSON is
returned.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour15/example07.
To test a JSON API with Mocha, follow these steps:
1. Returning to your json_api project in the test folder, create a file called
tasks.js and add the following content:
var http = require('http');
var assert = require('assert');
var app = require('../app.js');
Think about how the API should function. Are there any other things
you would add here? Going through this process is also useful for
documentation. At some point, you will need to share your API with
other developers, and going through these steps makes it easy to
write documentation.
The normal process here would be to write tests for these
descriptions first and then write code to make the tests pass just as
we did before. For brevity’s sake, you will not be going into the tests
for each of these actions, although you are encouraged to explore
them outside this hour.
Here is the implementation for each route. It follows closely the
techniques you used in Hour 8. First, create a new task:
This searches for a record by id and updates the record based on the
data sent in the PUT request. If there is a validation error, a 422
response is returned. If no document is found with the id, a 404
response is returned.
Here is the implementation for deleting a task:
app.del('/api/v1/tasks/:id', function(req, res){
Task.findById(req.params.id, function (err, doc){
if (doc) {
doc.remove(function() {
res.send(200)
});
} else {
res.json(404)
}
});
});
This searches for a record by id, deletes it if found, and returns a 404
response if it is not found.
If you have downloaded the code examples for this book, the
completed code for the API is available in hour15/example08.
This is a simplistic API (there is no authentication!), but it
demonstrates how easy it is to create JSON APIs with Node.js.
SUMMARY
In this hour, you were introduced to the idea of APIs and JSON as a
format for data exchange. You saw how to send JSON data with
Node.js and how easy it is to consume JSON with Node.js. You were
introduced to the idea of API clients and servers. First with the
Twitter example, you created a simple API client to fetch the latest
Node.js tweets; then you created a simple API for tasks.
You really only scratched the surface of what Node.js can do with
APIs in this hour, but hopefully you saw enough to convince you that
creating API clients and servers is straightforward and powerful in
Node.js. Coupled with the excellent performance and scalability of
Node.js, APIs are an excellent use case for the technology.
Q&A
Q. Why JSON and not XML?
A. JSON has become popular with web developers, especially those
that use JavaScript. When working with Node.js and JavaScript, it
makes sense to use JSON as a data format. Furthermore, in the
context of the web, JavaScript is now being used on the server and
client side. It makes sense to keep the data exchange format within
the JavaScript family if you are using JavaScript. It is faster to parse
and easier to use. You’ll find that most of the Node.js community
likes JSON. If you do want to use XML or another data format, you
can absolutely do that. It is just not covered in this hour.
Q. Is JSON smaller and faster than XML?
A. Typically, JSON does result in a smaller amount of data than
would be found with XML. Many servers compress data before it is
sent to the client, so in reality the difference in data size is negligible.
Q. Can I really use data that is published by third-party APIs?
A. Generally, the answer is yes. Particularly with public data (e.g.,
governments and public bodies), the data is effectively owned by
taxpayers so it should be in the public domain. Many organizations
have seen amazing things created by publishing their data in an API.
Some APIs do have licensing restrictions particularly around
commercial usage. If in doubt, review the license information for the
API or get in touch with the API provider directly.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. Why might you want to create an API?
2. What are some of the advantages of JSON over XML?
3. If you are writing JSON by hand, how can you ensure it is valid?
Quiz Answers
1. You might create an API as you want to provide access to data so
that it can be used. It can be a private API, meaning that only you or
whomever you give access to can use it. It can also be a public API,
meaning that anyone can use it.
2. JSON is a great choice if you are using Node.js because it is easy
to parse JSON. It is a lightweight data format that can handle
complex data structures and is well supported by browsers, too.
3. A number of online tools allow you to quickly validate your JSON.
These tools ensure that handwritten JSON will not break scripts, so
it is recommended that you validate handwritten JSON. Two
popular sites
are http://jsonlint.com/ and http://jsonformatter.curiousconcept.co
m/.
EXERCISES
1. Visit www.programmableweb.com/apis/directory and browse the
list of APIs. Filter the data format to JSON and explore the list.
Imagine what you could do with all this data!
2. Write a client to fetch some Node.js repositories from GitHub.
Perform a GET request on the following
URL: https://api.github.com/legacy/repos/search/node. Parse and
output the JSON in the response.
3. In the Tasks API that you created, review the file in test/tasks.js.
Try to understand the purpose of writing tests in this manner and
complete a test that checks for a 200 response. If you are feeling
ambitious, complete the test suite!
Hour 16. The Process Module
console.log(process.pid);
If you run this script from the console, you see the process id printed
out:
node process.js
32204
Running the script more than once, you will notice that each time it
is run, a new process id is assigned to it:
node process.js
32634
node process.js
32639
node process.js
32643
process.on('exit', function ()
// Do something when the script exits
});
process.stdin.resume();
process.on('SIGINT', function() {
console.log('Got a SIGINT. Exiting');
process.exit(0);
});
The first line of the script prevents the script from exiting as it
initializes reading from stdin so it does not exit. If the line was not
present, the script would finish and the process would end.
Receiving signals in Node.js follows the familiar callback pattern of
an anonymous function that is called when the process receives a
signal. If this script is run and then Ctrl+C is pressed on the
keyboard, a SIGINT is sent to the process and the script shows that a
SIGINT was received before exiting:
node process.js
[Press Ctrl-C on your keyboard]
Got a SIGINT. Exiting.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour16/example02.
Follow these steps to handle a SIGINT signal in the Process module:
1. Create a file called process.js and copy the following code into it:
process.stdin.resume();
process.on('SIGINT', function() {
console.log('Got a SIGINT. Exiting');
process.exit(0);
});
kill [process_id]
The kill command can also send other signals too. In the last
example, you saw how a process can be sent a SIGINT by pressing
Ctrl+C on the keyboard. The kill command can also send a SIGINT:
process.on('SIGINT', function() {
console.log('Got a SIGINT. Exiting');
process.exit(0);
});
process.on('SIGTERM', function() {
console.log('Got a SIGTERM. Exiting');
process.exit(0);
});
setInterval(function() {
// This keeps the process running
}, 10000);
In this example, the script listens for the SIGINT and SIGTERM
signals. The setInterval at the end keeps the script running. Without
it, the script would exit. If this file is saved as process.js, the script
can be run with the following command:
node process.js
process.on('SIGTERM', function() {
console.log('Got a SIGTERM. Exiting');
process.exit(0);
});
setInterval(function() {
// This keeps the process running
}, 10000);
#!/usr/bin/env node
Once you have placed the shebang on the first line of script, the
script also needs to be made executable. On Mac OSX or Linux, this
can be achieved with the following command:
chmod +x yourscript.js
With the shebang in place and ensuring the script is executable, you
can run the script as follows:
./yourscript.js
Note that you do not need to add the node command before you run
the script because this is encapsulated in the shebang. A simple
Node.js script is
#!/usr/bin/env node
console.log('my first node script!');
If this file is saved as script, it can be made executable and then run
to see the output:
chmod +x script
./script
my first node script!
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour16/example04.
Follow these steps to create an executable script with Node.js:
1. Create a file called script and copy the following code into it:
#!/usr/bin/env node
console.log('my first node script!');
2. Make the script executable:
chmod +x script
3. From the same directory as the script, run the following command to
execute the script:
./script
4. You should see the output from your script (see Figure 16.2).
Figure 16.2. Running a simple Node.js script
#!/usr/bin/env node
console.log(process.argv);
If this script is run with no arguments, you see that the first two
items of the array are already populated with 'node' and the path to
the script:
[ 'node',
'/Users/george/script' ]
If you pass arguments to the script, these are shown after these
elements in the array:
Using arguments scripts can become very flexible and useful. You
might want to write a script that fetches data from a public API, for
example. The following example demonstrates how you can write a
short Node.js script to fetch search results from Twitter’s search API
and show it in the console:
#!/usr/bin/env node
var http = require('http');
if (!process.argv[2]) {
console.error('A search term is required');
process.exit(1);
}
var options = {
host: 'search.twitter.com',
path: '/search.json?q=' + process.argv[2]
};
http.get(options, function(res) {
var data = "";
var json;
res.on("data", function (chunk) {
data += chunk;
});
res.on("end", function () {
json = JSON.parse(data);
for (i = 0; i < json.results.length; i++) {
console.log(json.results[i].text)
}
process.exit(0);
});
}).on('error', function(e) {
console.log("Error fetching data: " + e.message);
process.exit(1);
});
if (!process.argv[2]) {
console.error('A search term is required');
process.exit(1);
}
var options = {
host: 'search.twitter.com',
path: '/search.json?q=' + process.argv[2]
};
http.get(options, function(res) {
var data = "";
var json;
res.on("data", function (chunk) {
data += chunk;
});
res.on("end", function () {
json = JSON.parse(data);
for (i = 0; i < json.results.length; i++) {
console.log(json.results[i].text)
}
process.exit(0);
});
}).on('error', function(e) {
console.log("Error fetching data: " + e.message);
process.exit(1);
});
2. Make the script executable:
chmod +x script
3. From the same directory as the script file, run the script:
./script
4. You should see the script prompting you for a search term argument.
5. Run the script again with an argument:
./script node.js
6. You should see tweets about node.js returned from Twitter’s search API
(see Figure 16.3).
Figure 16.3. Passing arguments to a Node.js script
SUMMARY
In this hour, you were introduced to the Process module. You saw
how it provides a number of tools for developers to create and
interact with Node.js processes. You saw how it is possible to send
UNIX signals to Node.js scripts and how to create small, executable
scripts with Node.js. You can build on top of this knowledge to
create network scripts with Node.js that can be used and automated
to solve many problems.
Q&A
Q. What types of things can I use the Process module for?
A. The Process module is particularly useful for creating scripts with
Node.js. It can provide information on the environment that the
script is running in and allows signals to be sent to a process. It also
allows scripts to set the correct exit status allowing other scripts to
interact with them correctly. It is useful for most programming that
you do with Node.js, though, as everything is based on a process!
Q. Should I use Node.js for scripts over say Ruby, Python, or Bash?
A. Node.js excels at network programming, so if you are doing
anything involving networks in your scripts, Node.js is an excellent
choice. Node.js isn’t designed to be a general scripting language, so if
you have experience with other programming languages, you may
find that your purposes are better suited by another language. As
always, pick the right tool for the job.
Q. Can I use system commands in Node.js scripts?
A. Yes. You see how to do this in Hour 17, “The Child Process
Module.”
Q. Are there any modules available to help write Node.js scripts?
A. Yes. Commander is one Node.js module designed to ease
developing command-line
scripts: https://github.com/visionmedia/commander.js. There is
also the cli module at https://github.com/chriso/cli. More modules
are available that can be found at http://search.npmjs.org/.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. Do you need to do anything to use the Process module in your
scripts?
2. Why might you want to send signals to scripts?
3. How can you make sure a process stays up?
Quiz Answers
1. No. The Process module is a global object in Node.js, so you are
able to use it without adding anything to your scripts.
2. Signals are used in UNIX and UNIX-type systems to provide a
way for processes to communicate with each other. They can be used
to make a process terminate, suspend, or restart. Signals are a
POSIX standard meaning they are used by many other programs.
Using signals in Node.js allows you to integrate with other UNIX
tools if you want.
3. You might have realized that if you are working with a process,
you may be placing a heavy reliance on that script running. On
UNIX type systems, you can run scripts in the background, meaning
you don’t have to keep a terminal window open. There are also a
number of tools on UNIX, like monit and upstart, to help you
monitor processes and restart them if necessary. There are also tools
created with Node.js to help with this. One of the most popular ones
is forever (https://github.com/nodejitsu/forever/), which ensures a
script runs continuously.
EXERCISES
1. Open a terminal and run ps aux on Mac OSX or Linux
or tasklist /v on Windows. Try to understand what these processes
relate to. You might see the name of a program running on your
machine. Understand that there are many processes running on
your machine and that any Node.js process is just one of these.
2. Write a short command-line script that takes two arguments that
should be two numbers. The script should multiply the two numbers
and output the answer to the terminal. Think about the types of
validation that you need to do on the inputted data.
Hour 17. The Child Process Module
ping bbc.co.uk
You see a series of responses indicating how long the server took to
respond. The ping command has nothing to do with Node.js. It is a
tool on both Windows- and UNIX-type systems that allows you to
see whether a host or gateway is up by asking for an echo response.
The ping command allows you say, “Hey server! Are you up?” If it is
up, it responds, and the ping command shows you some statistics
about how long the round-trip took. Like most terminal-based
programs, ping expects some input and to send some output. If you
are interested in using the ping program within Node.js, you can do
so by spawning a child process and listening for the standard output
of the child process.
The first way to create a child process in Node.js is to use
the spawn() method. This takes a few arguments:
• Command—The command that you want to run
• Arguments—Any arguments that you want to give to the
command
• Options—Things like the working directory and environment
variables
In the case of using the ping utility, ping is the command
and 'bbc.co.uk' is the argument, so spawning a child process with the
ping utility is as follows:
This starts a child process, and as you saw when you ran
the ping command from your terminal, this returns some output to
the terminal or stdout (standard output). To be able to use the data
within your Node.js script, a listener must be added to the child
process to handle data that is received at standard output. Note here
that you must specify the encoding of the data or the raw stream will
be shown:
ping.stdout.setEncoding('utf8');
ping.stdout.on('data', function (data) {
console.log(data);
});
Now if this script is run, a child process will be spawned and the
standard output will be available to the parent process and shown in
the terminal:
PING bbc.co.uk (212.58.241.131): 56 data bytes
64 bytes from 212.58.241.131: icmp_seq=0 ttl=245 time=16.115 ms
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour17/example01.
Follow these steps to create a child process in Node.js:
1. Create a file called app.js and copy the following code into it:
var spawn = require('child_process').spawn;
var ping = spawn('ping', ['bbc.co.uk']);
ping.stdout.setEncoding('utf8');
ping.stdout.on('data', function (data) {
console.log(data);
});
2. Run the script:
node app.js
3. You should see the output from the child process in your terminal
(see Figure 17.1).
ping.stdout.setEncoding('utf8');
ping.stdout.on('data', function (data) {
console.log(data);
});
ping.kill('SIGINT');
Try It Yourself
Follow these steps to kill a child process:
1. Create a file called app.js and copy the following code into it:
var spawn = require('child_process').spawn;
var ping = spawn('ping', ['bbc.co.uk']);
ping.stdout.setEncoding('utf8');
ping.stdout.on('data', function (data) {
console.log(data);
});
ping.kill('SIGINT');
2. Run the script:
node app.js
3. You should see the child process being killed by the parent process.
In the child process, this message can be listened for and handled:
process.on('message', function(m) {
console.log('child process received message:', m);
});
The child process can also send messages back to the parent:
child.on('message', function(m) {
console.log('parent process received message:', m);
});
process.on('message', function(m) {
console.log('child process received message:', m);
});
Now when the parent process is run, it creates the child process and
messages will be sent back and forth:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour17/example03.
Follow these steps to use fork() and messaging between processes:
1. Create a new file called parent.js and copy in the following content:
var fork = require('child_process').fork;
var child = fork(__dirname + '/child.js');
child.on('message', function(m) {
console.log('parent process received message:', m);
});
child.send({ message: 'Hello child!' });
2. Create another new file called child.js and paste in the following content:
process.on('message', function(m) {
console.log('child process received message:', m);
});
3. Run the parent process:
node parent.js
4. You should see the communication between the parent and child
processes in your terminal (see Figure 17.2).
if (cluster.isMaster) {
console.log('Master process started with PID:', process.pid);
cluster.on('death', function(worker) {
console.log('worker ' + worker.pid + ' died');
cluster.fork();
});
} else {
console.log('Worker process started with PID:', process.pid);
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(3000);
}
if (cluster.isMaster) {
console.log('Master process started with PID:', process.pid);
cluster.on('death', function(worker) {
console.log('worker ' + worker.pid + ' died');
cluster.fork();
});
} else {
console.log('Worker process started with PID:', process.pid);
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(3000);
}
2. Start the server:
node app.js
3. You should see that one master and two child processes are started.
4. Note the pid of one of the child processes and, in another terminal
window or tab, issue a kill signal to the child process:
kill [pid number of child process]
5. Back in the terminal window running the server, you should see that the
process died and that the master process started another child process
(see Figure 17.3).
Figure 17.3. The Cluster module starting a pool of workers and
restarting a worker that died
SUMMARY
In this hour, you learned about the Child Process module. You saw
how to create a child process and how to run a system command in
Node.js using a child process. You saw how it is possible for a parent
process to kill a child process and how to send signals to child
processes. You learned about the fork() method that allows you to
create child Node.js processes and how to communicate between
Node.js processes. Finally, you saw how to use the Cluster module to
child processes and scale your application to number of cores
available on your server.
Q&A
Q. Should I be using system commands rather than trying to write
JavaScript with Node.js?
A. Yes. Sometimes, you will come up against a problem that has
already been solved with a mature, stable solution in another
programming language or a system tool. The example of using
ffmpeg to process videos is a good one where using another tool to
solve a problem is going to be much more stable and vastly quicker
than trying to write the code yourself.
Q. Are there any problems with using child processes and system
tools?
A. Portability is the main issue. If you write code for one operating
system, there is no guarantee that when the code is run on another
operating system that a particular system tool or piece of software
will be available. If portability is going to be an issue for the software
that you write, think carefully about using system-level tools.
Q. Do I need to use child processes?
A. Child processes should be used for specific purposes like being
able to use system tools, completing a long-running process, or
scaling an application to the number of processors on a computer.
For the majority of programming requirements, programming with
a single process is all you need.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. When should you use spawn(), and when should you use fork()?
2. What do you need to watch out for when you use spawn()?
3. When should you use the Cluster module over the Child Process
module?
Quiz Answers
1. You should use spawn() when you want to use a system command.
Use fork() to create another Node.js process.
2. If you are using a system command when you are developing, you
may find that when you deploy your application that the system
command is either not available or called something different. If you
use spawn(), make sure that Node.js has access to the command when
you deploy your application. If you are using Platform as a Service
provider, it is likely that system commands will not be available to
you.
3. You should use the Cluster module if you have a pool of child or
worker processes that you want a master process to manage. If you
need to scale your application to take advantage of the cores
available on your machine, the Cluster module provides a simple
way to do that.
EXERCISES
1. Find another system program that you use from the command
line and modify the code example that you can find at
hour17/example01 to output the results to the console. Try to
understand how arguments passed to the child process affect the
output.
2. Extend the example that you can find in hour17/example03 to
add further messaging between the parent and child script in
hour17/example01 by having the child script read the contents of a
file and sending the contents of the file as a message to the parent.
3. Experiment with the example available as hour17/example04 to
add more worker processes. Try sending kill signals to the worker
processes. What happens if you send a kill signal to the master
process?
Hour 18. The Events Module
If you are more familiar with jQuery, this is how the code would look
in jQuery:
$("#target").click(function() {
alert('Click event fired. Target was clicked!');
});
Both pieces of code are doing the same thing. They are setting up an
event listener for the click event on an HTML element with the id
of 'target'. When the click event happens, the event is said to be
fired, and an event listener handles the event by showing an alert.
The way that an event is handled is defined by the function within
the listener. In these examples, the event is emitted by a human
controlling a mouse. When the mouse is clicked, the event is fired.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour18/example01.
Follow these steps to fire events in JavaScript:
1. Create a file called index.html and copy the following code into it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>JavaScript Events</title>
</head>
<body>
<h1>JavaScript Events</h1>
<button id="target">Click me</button>
<script type="text/javascript">
var target = document.getElementById('target');
target.addEventListener('click', function () {
alert('Click event fired. Target was clicked!');
}, false);
</script>
</body>
</html>
Figure 18.1. A click event being fired and handled by an event listener
Once you have added this to your code, you can start to add events
and listeners. The Events module is actually simple, and if you can
understand emitting events and listening to events, you are a long
way to understanding how it works. If you have required the Events
module and created a new instance of EventEmitter(), you can emit an
event as follows:
The first argument is a string to describe the event and allow you to
match it up with a listener. You can use anything here. Once you
have labeled your event, you can add more arguments. You can have
more than one argument if you need it, and these will be passed into
the listener when the event is received. In this case, the second
argument is a string.
To receive the message, you must add a listener. This listens for the
event and handles it when it is fired. In this case, the second
argument of emit() is passed into the anonymous function as data so
that it may be used:
ee.on('message', function(data) {
console.log(data);
});
To explore this further, suppose that you are James Bond and have a
requirement to show a secret message and make it self-destruct in 5
seconds. By emitting and listening for events, a short Node.js script
can do this easily:
secretMessage.on('message', function(data) {
console.log(data);
});
setTimeout(function(){
secretMessage.emit('self destruct');
}, 5000);
In the script, there are two events emitted and two listeners. The
message event happens as soon as the script is run and is handled by
the 'message' handler. A setTimeout emits another event after 5
seconds and sends the 'self destruct' event. Note that there are no
additional arguments here—as additional arguments are optional. If
you run this script, you see the following:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour18/example02.
Follow these steps to emit and listen for events:
1. Create a file called app.js and copy the following code into it:
var EventEmitter = require('events').EventEmitter;
var secretMessage = new EventEmitter();
secretMessage.on('message', function(data) {
console.log(data);
});
setTimeout(function(){
secretMessage.emit('self destruct');
}, 5000);
2. Run the script:
node app.js
3. You should see the following output after a 5-second pause before the
second line:
This is a secret message. It will self destruct in 5 seconds..
BANG!! The message is destroyed!
Did You Know?: EventEmitter Is Used Everywhere in Node.js
EventEmitter is used in many other modules to handle events in Node.js. You
may recognize EventEmitter being used when you are reading files, creating
an HTTP server, or working with Streams. The code is relatively small, so if
you understand how to read JavaScript, it can be found
here: https://github.com/joyent/node/blob/master/lib/events.js. It is a
small piece of code with a big part to play in how Node.js works!
var options = {
host: 'stream.twitter.com',
auth: username + ":" + password,
path: '/1/statuses/filter.json?track=chocolate',
method: 'POST'
};
This is a simple HTTPS client, similar to the ones that you saw
in Hour 5, “HTTP,” in the HTTP module. What you should be
concerned with here is the following line in the client:
This is an event listener straight from Events module and allows the
client to receive the data when the event happens and do something
with it. In this case, the event that fires the listener is a network
event. It is the Twitter API that fires this event as new data is
received. In this context, you can see how the Event module can be
used to handle network events that can occur at any time and
respond to them as soon as they arrive. Dealing with network events
and I/O operations that are often outside the control of a script and
using events as a way to manage this is one of the core principles of
Node.js.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour18/example03.
The following steps demonstrate events with streaming data:
1. Create a file called app.js and copy the following code into it:
var https = require('https');
var username = 'YOUR_TWITTER_USERNAME';
var password = 'YOUR_TWITTER_PASSWORD';
var json;
var options = {
host: 'stream.twitter.com',
auth: username + ":" + password,
path: '/1/statuses/filter.json?track=chocolate',
method: 'POST'
};
pingPong.on('ping', function() {
console.log('Got ping');
setTimeout(function(){
pingPong.emit('pong');
}, 2000);
});
pingPong.on('pong', function() {
console.log('Got pong');
setTimeout(function(){
pingPong.emit('ping');
}, 2000);
});
The simple idea that you can use one event to fire off another one
allows complex functionality to be created, especially where a
cascade of events occurs. In this context, you can see how the Events
module can be used to build complex network programs that
respond to network or I/O events.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour18/example04.
Follow these steps to emit events with event listeners:
1. Create a new file called parent.js and paste in the following content:
var EventEmitter = require('events').EventEmitter;
var pingPong = new EventEmitter();
setTimeout(function(){
console.log('Sending first ping');
pingPong.emit('ping');
}, 2000);
pingPong.on('ping', function() {
console.log('Got ping');
setTimeout(function(){
pingPong.emit('pong');
}, 2000);
});
pingPong.on('pong', function() {
console.log('Got pong');
setTimeout(function(){
pingPong.emit('ping');
}, 2000);
});
2. Run the script:
node app.js
3. Watch how messages are sent back and forth between event handlers and
understand that the event handler is also an emitter. This keeps the ping
pong going (see Figure 18.3).
setTimeout(function(){
console.log('Added a second ping listener');
pingPong.on('ping', logPing);
}, 4000);
Now when the script is run after 4 seconds, the second listener will
be dynamically added and will log that it has received a ping
message to the terminal. In reality, you might want to use this to
write some logic that responds to a particular scenario based on
another event. Event listeners can also be removed by providing a
reference to the message and the callback function:
pingPong.removeListener('ping', logPing);
setTimeout(function(){
console.log('Removed second ping listener');
pingPong.removeListener('ping', logPing);
}, 12000);
So now when the script is run, there are events that are emitters and
also dynamic listeners that are added and removed during the life of
the process:
setTimeout(function(){
console.log('Sending first ping');
pingPong.emit('ping');
}, 2000);
pingPong.on('ping', function() {
console.log('Got ping');
setTimeout(function(){
pingPong.emit('pong');
}, 2000);
});
pingPong.on('pong', function() {
console.log('Got pong');
setTimeout(function(){
pingPong.emit('ping');
}, 2000);
});
setTimeout(function(){
console.log('Added a second ping listener');
pingPong.on('ping', logPing);
}, 4000);
setTimeout(function(){
console.log('Removed second ping listener');
pingPong.removeListener('ping', logPing);
}, 12000);
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour18/example05.
Follow these steps to manage events dynamically:
1. Create a new file called app.js and paste in the following content:
var EventEmitter = require('events').EventEmitter;
var pingPong = new EventEmitter();
setTimeout(function(){
console.log('Sending first ping');
pingPong.emit('ping');
}, 2000);
pingPong.on('ping', function() {
console.log('Got ping');
setTimeout(function(){
pingPong.emit('pong');
}, 2000);
});
pingPong.on('pong', function() {
console.log('Got pong');
setTimeout(function(){
pingPong.emit('ping');
}, 2000);
});
var logPing = function() {
console.log("Second ping listener got ping");
}
setTimeout(function(){
console.log('Added a second ping listener');
pingPong.on('ping', logPing);
}, 4000);
setTimeout(function(){
console.log('Removed second ping listener');
pingPong.removeListener('ping', logPing);
}, 12000);
2. Start the server:
node app.js
3. Watch how messages are sent back and forth and how a listener is added
and then removed dynamically (see Figure 18.4).
SUMMARY
In this hour, you were introduced to the Events module and how
events are used in Node.js. You played at being a James Bond by
creating a self-destructing message and then learned how events are
used in many other modules in Node.js. You saw how the HTTP and
HTTPS modules use events to receive data and saw an example of
this with Twitter’s streaming API. You played ping pong with a
Node.js script and saw how event listeners can also emit events to
allow scripts to be more intelligent and complex. Finally, you saw
that event listeners can be added and removed dynamically.
Q&A
Q. Is there a maximum number of listeners I can have for an event?
A. By default, events will emit a warning if you have more than 10
listeners. You can change this on a per-event basis
using emitter.setMaxListeners(n), however.
Q. Why don’t things happen in the order they appear in the code?
A. If you program around events, you are turning your script inside
out. You no longer program around the order that things appear in
your code but around the order that events happen. This is a good
thing!
Q. Is there a way to listen to all emitted events?
A. No. You need to create listeners for each event that you want to
respond to.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. How is programming around events different?
2. Why are events a good approach for network programming?
3. Why is JavaScript a good fit for the problems that Node.js is
trying to solve?
Quiz Answers
1. When you use events, your scripts are no longer executed in the
order that they are written in, as is the case with simple sequential
scripts. Instead, event listeners are used to respond to events that
occur, regardless of when they happen. This is liberating but initially
requires a change in thinking about how you approach your code.
2. When your application includes network calls, you are often
unable to predict exactly when something will return. As
applications become more complex, it becomes much more
manageable and efficient to structure code around events. When an
event happens, a listener can respond to it!
3. Because JavaScript is an event-driven programming language, it
is a great fit for the approach that Node.js takes to network
programming. JavaScript allows Node.js to provide an event-driven
API that many developers will already be familiar with.
EXERCISES
1. Read the documentation and examples on Node.js’s HTTP module
at http://nodejs.org/docs/latest/api/http.html. Can you recognize
where the Events module is being used and how events are used?
2. Create a short Node.js script that uses the Events module to emit
two different events. Create two event listeners to receive the events.
3. Extend the example that can be found at hour18/example05 to
dynamically add and then remove a second listener for the pong
event.
0123456789
Using these values, we can express numbers by using more than one
of these values and combining them together. Because there are 10
values, this system is known as Base 10. Numbers can also be
expressed in binary, but instead of having 10 values to express a
number, there are only two—1 and 0. Because there are two values,
this is known as Base 2.
By the Way: The Decimal System Is Base 10
The system we use to refer to numbers is commonly referred to as the
decimal system as there are 10 numbers, but it can also be referred to as Base
10.
Just like the Base 10 system, the value of a number in Binary (or
Base 2) depends on the position starting from the right. A bit’s value
grows by a power of two as it shifts from the right to the left:
1 1 1 1 1 1 1 1
128 64 32 16 8 4 2 1
Using Base 2, 8 bits all with the value of 1 (or 11111111) is 255. A
group of 8 bits is referred to as a byte, and it can be used to store a
value that represents a letter or a number. As you just saw, the
largest number you can declare with eight bits is 255. With eight
values of 0, you can declare a 0, so there are 256 different values you
can declare with a byte. Bytes are sometimes also referred to as
octets, as there are 8 bits, and they form the basis for holding a
character of text.
By the Way: 4 Bits Are a Nibble
4 bits, or half a byte, is referred to as a nibble. Because a nibble is four bits, it
can have 16 possible values. This can also be referred to as hexadecimal, hex,
or Base 16, all because there are 16 possible values!
BINARY TO TEXT
Although computing operates on a system of 0s and 1s, humans do
not. As such, a number of encoding systems exist to convert binary
data to text. One of these is ASCII (American Standard Code for
Information Interchange). This is based on the English alphabet and
provides a way to encode the alphabetic, numeric, and punctuation
characters commonly used in English. The number of possible
different characters in ASCII is 128 (0-127). You may recall from
learning about the Base 2 system that the number 128 is significant:
It is the maximum number that you can declare with 7 bits. This
provides a system to declare English language characters. For
example, in ASCII, the character ‘A’ is the number 65 in the decimal
system or 1000001 in binary.
ASCII was designed for the English language, but evidently the
world does not just use English or English language characters to
communicate. In recent years, the encoding of choice for the web has
become UTF-8. This can represent every character in the Unicode
set that encompasses the majority of the languages and character
sets in use in the world today. UTF-8 uses between 1 and 4 bytes to
express characters and is fully backward compatible with ASCII.
Characters that are expected to be used frequently are encoded using
fewer bytes (usually one), whereas characters that are unusual are
encoded using four.
BINARY AND NODE.JS
JavaScript was originally designed for browsers and so works well
with unicode-encoded strings (human-readable text) but does not
handle binary data well. This is a problem for Node.js, because it is
designed to send and receive data over networks that often will be in
binary format. Some examples of where transmitting binary data is
needed are
• Sending and receiving data over a TCP connection
• Reading binary data from an image or compressed file
• Reading and writing data from a filesystem
• Dealing with streams of binary data from networks
The Buffer module gives Node.js a way of storing raw data so that it
can be used in the context of JavaScript. Whenever you work with
moving data in I/O operations in Node.js, it is likely that the Buffer
module is being used. For example, when you read a file, Node.js
uses a Buffer object to hold the data from the file. You can see this by
creating a text file called file.txt, writing some text in it, and then
creating a Node.js script to read the file:
var fs = require('fs');
fs.readFile('file.txt', function(err,data){
if (err) throw err;
console.log(data);
});
If you run this script, you see that the data logged is actually a Buffer
object:
<Buffer 23 23 0a 23 20 55 73 65 72 20 44 61..
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour19/example01.
Follow these steps to show a Buffer object:
1. Create a file called app.js and copy the following code into it:
var fs = require('fs');
fs.readFile('file.txt', function(err,data){
if (err) throw err;
console.log(data);
});
2. Create a file called file.txt and add the following content to it:
It is the future,
The distant future
It is the distant future,
The year 2000
We are robots
3. Run the script:
node app.js
4. You should see the raw Buffer object printed to the console (see Figure
19.1).
Figure 19.1. Showing a raw buffer data from reading a file
var fs = require('fs');
Now, rather than the raw buffer, the contents of the file are shown:
It is the future,
The distant future
It is the distant future,
The year 2000
We are robots
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour19/example02.
Follow these steps to set the encoding for a buffer:
1. Create a file called app.js and copy the following code into it:
var fs = require('fs');
We are robots
3. Run the script:
node app.js
4. You should see the contents of the file rather than the raw buffer printed
to the terminal (see Figure 19.2).
buffer
<Buffer cc cc cc cc cc cc ff 75>
buffer.toString()
'???????u'
Note that you may see different characters than the example, as
buffer is a representation of the raw memory allocated on your
machine.
You may also create a new Buffer object and pass in an array of bytes
or octets. You may recall from earlier that 85 equates to the
character ‘U’. So creating a buffer with the value 85 results in the
UTF-8 encoded contents being the character U:
Try It Yourself
Follow these steps to create a buffer with an array of bytes:
1. Start the Node.js REPL by typing node at a console.
2. Initialize a buffer with an octet:
If you know the string that you want to encode, you can also
instantiate a new Buffer object and pass the string directly into it:
This writes the character “a” into your buffer, and node returns the
number of bytes that were written to the buffer after encoding. The
UTF-8 encoding of the letter “a” takes 1 byte, but other characters
use more than 1 byte. For example, UTF-8 also has a number of
symbols such as the black telephone ( ).
To encode this character takes 3 bytes:
Try It Yourself
Follow these steps to initialize a buffer with a string:
1. Start the Node.js REPL by typing node at a console.
2. Initialize a buffer of 8 bytes:
var buffer = new Buffer(8)
3. Write a “c” character to the buffer with UTF-8 encoding:
buffer.write('c', 'utf8')
4. You should see that the encoding took 1 byte.
APPENDING TO BUFFERS
Buffer objects are often used to buffer data that is received so that all
the data can be used once it has been received. This means that
when you write to a buffer you need to be able to append data to it.
By default, when you write to a buffer, it writes to the first byte, or
the start of the buffer. This can be illustrated as follows:
Note that you may see different characters than the example, as
Buffer is a representation of the raw memory allocated on your
machine.
This is obviously not what you want! When appending to a buffer,
you can pass an option offset argument that writes to the buffer at
the given byte or octet:
Try It Yourself
Follow these steps to append to a buffer:
1. Start the Node.js REPL by typing node at a console.
2. Initialize a buffer of 8 bytes:
var buffer = new Buffer(8)
3. Write a string to the buffer with UTF-8 encoding:
buffer.write('hi', 'utf8')
4. Examine the contents of the buffer:
buffer.toString()
5. Write another string to the buffer with an offset:
buffer.write(' there', 2, 'utf8')
6. Examine the contents of the buffer:
buffer.toString()
7. You should see that the second string was appended to the first one.
COPYING BUFFERS
Node.js provides a way to copy all or parts of a Buffer object into
another Buffer object. You may only copy between Buffer objects
that already exist, so you need to create them first. To copy from one
buffer to another is as follows, where buffer is a Buffer object you
want to copy from and bufferToCopyTo is a Buffer object you want to
copy to:
buffer.copy(bufferToCopyTo)
You may not want to copy all your Buffer object into another one, so
you may also specify what you want to copy from your buffer into the
target buffer. The Buffer module also allows you to specify where in
the target buffer you copy to:
buffer.copy(bufferToCopyTo, 2, 3, 8)
The second argument specifies which byte that the copying should
be written to in the target buffer. The third and fourth arguments
specify where the copy should start and stop in the source buffer.
This can be illustrated as follows:
Carr, Jimmy
Smith, Arthur
Bailey, Bill
The sort utility in UNIX receives lines of text, sorts them, and then
returns the sorted version. It expects the data that it will operate on
comes from standard input and the result to be sent to standard
output. So, to sort the file, you can redirect standard input to
the sort command, as follows:
4. Sort the file again, but this time, redirect the output to a file.
sort < names.txt > sorted_names.txt
5. The file sorted_names.txt should have been created and contain the
sorted results of names.txt.
READABLE STREAMS
In Hour 19, “The Buffer Module,” you learned about buffers and saw
how they are a fundamental piece of the architecture of Node.js. If
buffers are the way that Node.js handles raw data, streams are
usually the way that Node.js moves that data around. Streams in
Node.js can be readable and/or writable, and this loosely
corresponds to standard input and standard output that you saw
earlier, with standard input being readable and standard output
being writable. Streams are used in many other modules in Node.js,
including HTTP and File System.
You can see this in action in the File System module that uses
streams to read and write file data. Suppose that you want to read
the list of names that you saw earlier from a file called names.txt so
that the data can be used. The File System module allows you to
create a readable stream to read this data in. Because the data is
streamed, it means you can start acting on the data even before the
file has finished being read and as soon as the first bytes of data are
received. This is a common pattern in Node.js:
var fs = require('fs');
var stream = fs.ReadStream('names.txt');
stream.setEncoding('utf8');
stream.on('data', function(chunk) {
console.log('read some data')
});
stream.on('close', function () {
console.log('all the data is read')
});
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour20/example02.
To use streams to read a file, follow these steps:
1. Create a file called app.js and copy the following code into it:
var fs = require('fs');
var stream = fs.ReadStream('names.txt');
stream.setEncoding('utf8');
stream.on('data', function(chunk) {
console.log('read some data')
});
stream.on('close', function () {
console.log('all the data is read')
});
2. Create a file called names.txt and copy the following content into it:
Carr, Jimmy
Smith, Arthur
Bailey, Bill
3. Run the script:
node app.js
4. You should see the following in the terminal window:
read some data
all the data is read
This shows that the two events were fired. Some data was received
and the file finished being read. But, where are the names? This is an
important point to note: With streams, you are responsible for using
the data in whatever way you want, so you have to handle the data
when it is received on the data event. If you want to read all the data,
you must concatenate it into a variable:
var fs = require('fs');
var stream = fs.ReadStream('names.txt');
var data = '';
stream.setEncoding('utf8');
stream.on('data', function(chunk) {
data += chunk;
console.log('read some data')
});
stream.on('close', function () {
console.log('all the data is read')
console.log(data);
});
Now if you run the script, you see the events happening and the
names being shown:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour20/example03.
Follow these steps to concatenate data from a stream:
1. Create a file called app.js and copy the following code into it:
var fs = require('fs');
var stream = fs.ReadStream('names.txt');
var data = '';
stream.setEncoding('utf8');
stream.on('data', function(chunk) {
data += chunk;
console.log('read some data')
});
stream.on('close', function () {
console.log('all the data is read')
console.log(data);
});
2. Create a file called names.txt and copy the following content into it:
Carr, Jimmy
Smith, Arthur
Bailey, Bill
3. Run the script:
node app.js
4. You should see the following in the terminal window (see Figure 20.2):
read some data
all the data is read
Carr, Jimmy
Smith, Arthur
Bailey, Bill
In the case of a list of three names, reading the file is very quick, and
there is only one data event. But, it may be the case that a file is large
and there may be more than one event. This allows developers to do
something with the data as soon as it is received rather than wait for
an entire file to be read. For example, if a larger text file is read into
the readable stream, it generates more than one data event. The
website http://www.lipsum.com/ allows generation of Latin text.
For the next example, this site is used to generate 1,000 paragraphs
and then those paragraphs are copied into a file called latin.txt. As
before, a short Node.js script is used to read this file using a readable
stream:
var fs = require('fs');
var stream = fs.ReadStream('latin.txt');
stream.setEncoding('utf-8');
stream.on('data', function(chunk) {
console.log('read some data')
});
stream.on('close', function () {
console.log('all the data is read')
});
This time, when the script is run, you see more than one data event.
Because the file is larger, data is read in when it is received,
triggering more than one data event and meaning that it can be used
straightaway. Streams make it possible to start operating on data as
soon as it is received, regardless of how big the file is:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour20/example04.
The following steps demonstrate data events:
1. Create a file called app.js and copy the following code into it:
var fs = require('fs');
var stream = fs.ReadStream('latin.txt');
stream.setEncoding('utf-8');
stream.on('data', function(chunk) {
console.log('read some data')
});
stream.on('close', function () {
console.log('all the data is read')
});
2. Visit http://www.lipsum.com/ and generate 1,000 paragraphs of Latin
text. Copy the lines into a file called latin.txt.
3. Run the script:
node app.js
4. You should see the following in the terminal window (see Figure 20.3):
read some data
read some data
read some data
all the data is read
Figure 20.3. A readable stream with multiple data events being fired
WRITABLE STREAMS
As you might expect, you may also create writable streams so that
you can write data. This means that, with a simple script, you can
now use streams to read a file and write it into another one:
var fs = require('fs');
var readableStream = fs.ReadStream('names.txt');
var writableStream = fs.WriteStream('out.txt');
readableStream.setEncoding('utf8');
readableStream.on('data', function(chunk) {
writableStream.write(chunk);
});
readableStream.on('close', function () {
writableStream.end();
});
readableStream.setEncoding('utf8');
readableStream.on('data', function(chunk) {
writableStream.write(chunk);
});
readableStream.on('close', function () {
writableStream.end();
});
2. Create a file called names.txt and copy the following content into it:
Carr, Jimmy
Smith, Arthur
Bailey, Bill
3. Run the script:
node app.js
4. The content of names.txt should be copied into out.txt.
PIPING STREAMS
Because piping data between an input and an output is common in
Node.js, it also provides a way to connect two readable and writable
streams and pipe data between them. This is the pipe() method:
var fs = require('fs');
var readableStream = fs.ReadStream('names.txt');
var writableStream = fs.WriteStream('out.txt');
readableStream.pipe(writableStream);
STREAMING MP3S
Streams can also be used in conjunction with the HTTP module that
also uses streams for many objects. For this example, you create a
streaming MP3 server. The MP3 used is a file
from http://www.danosongs.com/. Dan-O is a musician who
publishes his music online under a Creative Commons license. The
file you will use can be downloaded
from http://www.danosongs.com/music/danosongs.com-
rapidarc.mp3, and it is included in the book’s code examples.
In the HTTP module, the response object is actually a writable
stream. This allows a file to be read as a readable stream and piped
through to the writeable stream of the response object, just as you
saw with copying a file. Because pipe() handles all the necessary
pausing and resuming, this allows a streaming MP3 server to be
created in a few lines of code:
http.createServer(function(request, response) {
var mp3 = 'danosongs.com-rapidarc.mp3';
var stat = fs.statSync(mp3);
response.writeHead(200, {
'Content-Type': 'audio/mpeg',
'Content-Length': stat.size
});
}).listen(3000);
The HTTP module should be familiar to you from Hour 5, “HTTP,”
and using streams allows the MP3 file to read and then pipe out to
the response. If you start up the server and browse to port 3000—if
your browser supports playing MP3s—you will hear the MP3 being
streamed.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour20/example07.
The following steps show you how to create a streaming MP3 server:
1. Create a file called app.js and copy the following code into it:
var http = require('http'),
fs = require('fs');
http.createServer(function(request, response) {
var mp3 = 'danosongs.com-rapidarc.mp3';
var stat = fs.statSync(mp3);
response.writeHead(200, {
'Content-Type': 'audio/mpeg',
'Content-Length': stat.size
});
}).listen(3000);
2. Run the script:
node app.js
3. Open a browser at http;//127.0.0.1:3000/. If your browser supports
playing mp3s, you will hear the mp3 being streamed.
SUMMARY
In this hour, you learned one of the more advanced concepts within
Node.js: streams. You saw how Node.js streams work similarly to
how streams work in UNIX type systems and how they are used in
Node.js. You were introduced to how streams are used in the File
System module and learned how to read a file using a readable
stream. You then saw how to create a writable stream within the File
System module and saw how to copy a file in Node.js using streams.
You learned how to use the pipe() method as a shorthand for moving
data between a readable stream and a writable stream. Finally, you
saw how to create a streaming MP3 server in a few lines of code and
learned how many objects in Node are writable streams, including
http.ServerResponse, allowing readable streams to be piped directly
into it.
Q&A
Q. What can I use streams for?
A. Streams can be used to create proxy servers, streaming servers, or
services that manipulate file uploads, like image resizing or video
transcoding.
Q. When should I handle events myself and when should I
use pipe()?
A. You can use pipe() to connect a readable stream and a writable
stream and this handles pausing and resuming for you. If you need
greater control over pausing and resuming, you can handle these
events yourself, but for most use cases, pipe() takes care of
everything for you.
Q. Streams seem more complicated than buffers!
A. Streams are part of the approach that Node.js takes to network
programming, so it is important to understand them and how they
work. For your day-to-day programming, you will probably use
streams (and subsequently buffers) all the time but not be aware of
them. As streams are fundamental to how Node.js works, it is
important to at least have an overview of how they work.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. Why are streams an efficient way of moving data around?
2. Is it possible to pipe to more than one writable stream?
3. What type of things are streams useful for?
Quiz Answers
1. Because data from streams can be read as soon as it is ready, data
can be used before an operation is entirely complete. This means
data can get from A to B much more quickly and that developers can
start operating on chunks of data as soon as they are ready.
2. Yes. You can pipe data through to multiple streams. An example
of where this might be useful is with a file upload that can be piped
to a writable stream that uploads to Amazon S3 and another one that
saves the file to disk.
3. Streams are useful whenever you want to move data around and
need to operate on the data as soon as you can. Examples include an
image resizing server, dealing with large text files, or an HTTP
server.
EXERCISES
1. Create a script with the HTTP module to download the following
file: http://releases.ubuntu.com/lucid/ubuntu-10.04.4-desktop-
i386.iso. Use the data event to log a message to the console each
time a data event is received. Note how data is made available as
soon as it is received.
2. Create two HTTP servers and proxy traffic from one to the other
one using streams.
Hour 21. CoffeeScript
Then, in CoffeeScript:
From first impressions, you can see that CoffeeScript removes things
like curly braces and semicolons from the JavaScript version.
You learn about the features of CoffeeScript in this hour, but to start
using CoffeeScript, try it in your browser
at http://jashkenas.github.com/coffee-script/.
Try It Yourself
Follow these steps to get started with CoffeeScript:
1. Open a browser and visit http://jashkenas.github.com/coffee-script/.
2. Click Try CoffeeScript. You are now in an interactive editor with
CoffeeScript on the left and JavaScript on the right. As you enter
CoffeeScript on the left, the equivalent JavaScript is shown on the right.
3. In the left column, type
breakfast = 'eggs'
4. In the right column, the equivalent JavaScript is shown (see Figure 21.1).
You should see
var breakfast;
breakfast = 'eggs';
Congratulations! You just wrote CoffeeScript and compiled it to JavaScript!
Figure 21.1. Using CoffeeScript in the browser
coffee --help
coffee -c app.coffee
node app.js
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour21/example01.
To run CoffeeScript, follow these steps:
1. Create a file called app.coffee and copy the following code into it:
http = require 'http'
http.createServer (req, res) ->
res.writeHead 200, 'Content-Type': 'text/plain'
res.end 'Hello, World!\n'
.listen 3000, '127.0.0.1'
console.log 'Server running at http://127.0.0.1:3000/'
2. Save the file.
3. Assuming you have installed CoffeeScript globally with npm (npm install
–g coffee-script), start the server with
coffee app.coffee
4. Open a browser at http://127.0.0.1:3000.
5. You should see “Hello, World!”
In the example, you used the coffee command to run a CoffeeScript
file directly without pre-compiling it to JavaScript. Within the
Node.js community, this is the most common way of using
CoffeeScript.
It is also possible to pre-compile CoffeeScript files to JavaScript and
then execute the JavaScript files as you would normally.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour21/example02.
Follow these steps to compile CoffeeScript to JavaScript:
1. Return to the app.coffee file that you created.
2. From the terminal, run the following command:
coffee -c app.coffee
3. Note that a new file is created in the same folder called app.js.
4. Examine the contents of the file app.js.
5. Start the server using the JavaScript file:
node app.js
6. Open a browser at http://127.0.0.1:3000.
7. You should see “Hello, World!”
In this example, rather than running the script directly from
CoffeeScript, the file is first pre-compiled to JavaScript. If you
examine the JavaScript file that is generated, you will notice that the
script looks different from the basic Hello World JavaScript example
that you saw in Hour 1, “Introducing Node.js.” This is because
CoffeeScript applies some opinions about how to best write
JavaScript. Generally, this is a good thing, but as you learn later in
this hour, this is the first of many things that can irritate developers
about CoffeeScript.
WHY USE A PRE-COMPILER?
At this stage, you may well be thinking, “Why do I need this? It adds
complexity!” If that’s the case, you should read to the end of this
hour, see what CoffeeScript can offer you, and then make up your
mind.
Some reasons for using a pre-compiler include
• To avoid common syntax errors
• To add features to programming languages that are not natively
present
• To improve code syntax and readability
• To take advantage of coding best practices included in a pre-
compiler
If you have written CSS, you may have come into contact with a CSS
pre-compiler, like Sass or LESS. Both of these tools do a similar job
to CoffeeScript. They iron out some of the more bumpy parts of
using CSS in real life. A great example of this is variables within CSS.
CSS has no support for declaring variables, so if you have a color
palette, you have to declare the color value every time you want to
use it. This is fine in theory until you decide to change the color
palette and you have to change the color in multiple places. By using
a pre-compiler like Sass, you can set a single variable and then use it
over and over again, but just have one single place where the color is
set.
In Hour 7, “More on Express,” you were introduced to Jade, a
template engine. One feature of template engines is that they allow
you to pre-compile HTML. If you have ever written HTML by hand,
you will have inevitably found a scenario where you forgot to close
an HTML tag, causing your layout to be broken. By using an HTML
pre-compiler, you can avoid issues like this.
Tools like Sass and Haml both make creating CSS and HTML easier
for the developer. You do not need them, but you can choose to use
them to improve your productivity. CoffeeScript is similar; it can
offer you productivity improvements in return for a little more
complexity.
FEATURES OF COFFEESCRIPT
This section guides you through the features of CoffeeScript and lets
you try examples.
Minimal Syntax
A major feature of CoffeeScript is to provide a minimal syntax
version of JavaScript. JavaScript’s syntax is often criticized for being
too verbose and complex. Specific points that often come up are
• Semicolons are everywhere and forgetting them introduces errors.
• You have to declare the function keyword over and over again.
• Trailing commas can introduce a number of gotchas.
• Variable scope in JavaScript is easy to get wrong.
Experienced JavaScript programmers would say that many of these
accusations are unfair, but for a programmer coming from an object-
oriented language, like Python or Ruby, these points are
commonplace. It is no surprise then that CoffeeScript derives many
features of its syntax from Python and Ruby.
To examine how this works, consider the following JavaScript:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour21/example04.
Here’s how to cube a number in CoffeeScript:
1. Create a new file called app.coffee and paste in the following code:
cube = (x) -> x * x * x
console.log cube 3
2. From the terminal, run the script:
coffee app.coffee
3. You should see 27.
Conditions and Comparisons
As well as making JavaScript syntax a great deal shorter,
CoffeeScript adds a number of new ways to do comparisons.
Consider the following JavaScript statement that is a common
pattern in JavaScript to check whether a variable exists and is not
null:
if audience
sing song
if (audience){
sing(song);
}
start() if light is on
stop() if light isnt on
Loops
In JavaScript, for loops are verbose when compared to other
languages. Consider this JavaScript:
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour21/example05.
The following steps show a loop in CoffeeScript:
1. Create a file called app.coffee and copy the following code into it:
console.log food for food in ['Maroilles','Brie de Meaux','Stinking Bishop']
2. Save the file.
3. From the terminal, run the script:
coffee app.coffee
4. You should see a list of cheeses printed in the terminal.
Strings
CoffeeScript adds some string features that make working with
strings simpler. Often, when building a string, you want to use a
variable within a string. You may have seen JavaScript like this:
Particularly when you have many different variables that you want to
include, this becomes a useful technique.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour21/example06.
Here’s how to use string interpolation in CoffeeScript:
1. Create a file called app.coffee and copy the following code into it:
movie = "Willy Wonka & The Chocolate Factory"
string = "My favorite Movie is #{movie}"
console.log string
2. Save the file.
3. Change the movie to be your favorite movie.
4. From the terminal, run the script:
coffee app.coffee
5. You should see your favorite movie printed to the terminal.
CoffeeScript also supports the Heredocs style of string declaration.
In native JavaScript, you must escape quotes and apostrophes. The
Heredocs style made available via CoffeeScript makes this much
more convenient.
In JavaScript, you might write some HTML like this:
var html;
html = "<p>\n My awesome HTML\n</p>";
Note that you must use the special \n character to denote a line
break. In CoffeeScript, you may write this as you would see it in your
HTML:
html = """
<p>
My awesome HTML
</p>
"""
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour21/example07.
Here’s how to use Heredocs In CoffeeScript:
1. Create a file called app.coffee and copy the following code into it:
html = """
<p>
Hello World!
</p>
"""
console.log html
2. Save the file.
3. From the terminal, run the script:
coffee app.coffee
4. You should see the HTML outputted with correct indentation and with
new lines.
Objects
CoffeeScript supports the creation of objects through YAML (Yet
Another Markup Language) style syntax. In JavaScript, an object
might be written as follows:
var kids;
kids = {
brother: {
name: "Max",
age: 11
},
sister: {
name: "Ida",
age: 9
}
}
kids =
brother:
name: "Max"
age: 11
sister:
name: "Ida"
age: 9
class Car
constructor: (@name) ->
class Car
constructor: (@name) ->
mileage: 81000
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour21/example08.
The following steps show class constructors in CoffeeScript:
1. Create a file called app.coffee and copy the following code into it:
class Bird
constructor: (@name) ->
2. In the same app.coffee file, add the following lines:
bird = new Bird("Robin")
console.log "The bird is a #{bird.name}!"
3. From the terminal, run the script:
coffee app.coffee
4. You should see the following output:
The bird is a Robin!
Subclassing is used regularly in object-oriented programming where
a class inherits from another class. CoffeeScript supports
inheritance, meaning it is possible to create classes that inherit
properties of the parent class. To inherit from another class, use
the extends keyword:
class Human
constructor: (@legs = 2) ->
growLeg: ->
@legs++
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour21/example09.
The following steps show how to use classes in CoffeeScript:
1. Create a file called app.coffee and copy the following code into it:
class Human
constructor: (@legs = 2) ->
growLeg: ->
@legs++
class Robot
makeTea: ->
console.log 'Making tea.'
coffee -c app.coffee
coffee -c app.coffee
node app.js
But, you may also run the script directly and skip the compilation to
JavaScript:
coffee app.coffee
Although this will catch syntax errors, you will still have to debug
other issues directly in CoffeeScript, and there are times where you
may want to compile to JavaScript to see exactly what is happening.
A few benchmarks are available, but anecdotal reports from
CoffeeScript’s creator suggest that there is little performance hit in
not compiling CoffeeScript to JavaScript.
REACTIONS TO COFFEESCRIPT
In the UK, there is a yeast extract called Marmite that people spread
on their toast for breakfast. Marmite causes strong reactions—you
either love it or you hate it. Within the Node.js community, the
reaction to CoffeeScript is the same. If you want to test this out, drop
into the #node.js IRC channel and ask what people think of
CoffeeScript. Expect a healthy debate!
For those switching from languages like Python and Ruby,
CoffeeScript is generally loved. The syntax is familiar, and there is
no need to spend years learning all the quirks of JavaScript.
CoffeeScript does all the hard work with a syntax that is familiar,
outputting a curated version of JavaScript that avoids many
common programming pitfalls.
For those with little programming experience, CoffeeScript can be
unpopular. It is another thing to learn in a daunting list of software
tools. Particularly for someone who has some experience with
JavaScript through jQuery, the additional step to compile to
JavaScript can seem slow and unnecessary.
For the experienced JavaScript programmer, reactions can be
mixed. These programmers have spent many years understanding
the intricacies of JavaScript and many hours of painful debugging!
Although CoffeeScript avoids many common JavaScript problems, it
outputs opinionated JavaScript that may not be in line with the style
of an experienced JavaScript developer. Furthermore, some see
CoffeeScript as an irrelevant complication if they are already a highly
skilled JavaScript developer. It is often seen as a toy to help
inexperienced developers.
In addition to debugging being more complex, it is suggested that by
writing your Node.js project in CoffeeScript, it will be much harder
to work with other developers. By using CoffeeScript, you are
assuming that anyone else on the project will understand
CoffeeScript and pre-compiling JavaScript. The accusation is that
CoffeeScript adds another barrier for people wanting to collaborate
on your project.
Brendan Eich, the creator of JavaScript, wrote the following on his
website (http://brendaneich.com/2011/01/harmony-of-my-
dreams/):
CoffeeScript is well done and more convenient to use than JS,
provided you buy into the Python-esque significant space and the
costs of generating JS from another source language.
This is a telling quote. CoffeeScript is more convenient than
JavaScript, but it comes at the cost of learning a new syntax and an
extra compilation step.
SUMMARY
In this hour, you learned about CoffeeScript, a pre-compiler for
JavaScript. You learned that you can use it with Node.js projects and
examined features like syntax, loops, objects, and classes. You
learned about the pros and cons of using CoffeeScript as opposed to
writing native JavaScript. You covered the productivity boosts that
CoffeeScript can offer, issues with debugging and collaborating, and
how CoffeeScript sidesteps some of the more difficult parts of the
JavaScript programming language.
Q&A
Q. So get off the fence! Should I use CoffeeScript?
A. I am staying on the fence! The short answer is it depends. It
depends on your level of JavaScript expertise, whether you are
collaborating with other developers who know CoffeeScript, and
whether you even like CoffeeScript. CoffeeScript can certainly offer a
lot, but it is not without some potential issues.
Q. Is CoffeeScript specific to Node.js?
A. No. CoffeeScript can be used in the browser and within many
other programming languages. For example, the 3.1 release of Ruby
on Rails introduced CoffeeScript as the default way to write
JavaScript within Rails applications. Wherever you can use
JavaScript, you can (probably) use CoffeeScript.
Q. Why all the controversy around CoffeeScript?
A. CoffeeScript is a hugely disruptive, yet tiny language. It
potentially removes the need for some developers to ever write
JavaScript again. For developers who have built a career on writing
JavaScript and understanding all the difficult parts of the language,
this is a challenge. These developers are certainly going to point out
the flaws with CoffeeScript and rightly so. Conversely, for those who
do not have a high level of JavaScript experience, CoffeeScript is
liberating. It enables access to murky parts of JavaScript that only
expert-level programmers understand. These users of CoffeeScript
are going to want to write everything in CoffeeScript. This is usually
the argument—if you love CoffeeScript, you want to use it for
everything. If you see CoffeeScript as a toy and something that
potentially challenges years of learning, you are going to hate it.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. What are some of the reasons that CoffeeScript was created?
2. What are some advantages of using CoffeeScript?
3. What are some of the potential issues of using CoffeeScript in a
project?
Quiz Answers
1. CoffeeScript was created to abstract some of the more difficult
parts of writing applications using JavaScript. It was created to
provide developers with a cleaner syntax and add some additional
features to JavaScript from other programming languages.
2. By using CoffeeScript, your source code will be more concise.
Some may argue that it is easier to understand. You can make use of
some features not present in the JavaScript language, so potentially
you can solve problems more quickly. If you prefer using
CoffeeScript over JavaScript, you will also be a happier developer!
3. Using CoffeeScript commits you to using a pre-compiler in your
project. If you are working with other developers, you may find that
some hate or even refuse to use CoffeeScript. For less experienced
developers, adding a pre-compiler adds another layer to learn.
EXERCISES
1. Return to http://jashkenas.github.com/coffee-script and click Try
CoffeeScript. Experiment with entering CoffeeScript in the left panel
and seeing the output in the right panel. Try setting a variable and
then creating a simple class to represent an animal. If you get stuck,
there are examples on the home page.
2. Write a small web server in CoffeeScript using the Node.js HTTP
module. Create three different GET request routes. Run the script
using the coffee command.
3. To become familiar with larger CoffeeScript projects,
visit https://github.com/github/hubot. Examine the CoffeeScript
files in the src folder of the project. Try to understand some of the
source code and recognize classes being used. Do not worry if you do
not understand all the code—it is a complex project! If you want to
learn more about CoffeeScript, a free book is available
at http://arcturo.github.com/library/coffeescript/.
npm init
FOLDER STRUCTURE
No folder structure is enforced for Node.js modules, but many
developers use a common pattern that you may want to adhere to. If
you choose to use the folder structure suggested below, just use the
folders relevant to your project. So, if you don’t have any
documentation, do not use the doc folder! You may also want to add
additional folders or files depending on the complexity of your
project. For a very small library, you may want to not use any folders
at all and place the code for your module in a single index.js file. It is
up to you to choose the best approach, but bear in mind that if you
are working with other developers, following some kind of
convention makes it easier for them to develop with you. A
suggested folder structure is as follows:
• .gitignore—List of files to ignore from your Git repository
• .npmignore—List of files to exclude from the npm registry
• LICENSE—License file for the module
• README.md—README file for the module written in the
Markdown format
• bin—Folder that holds an executable for the module
• doc—Folder that holds documentation for the module
• examples—Folder to hold practical examples of how to use the
module
• lib—Folder to hold the code for the module
• man—Folder to hold any man pages for the module
• package.json—JSON file describing the module
• src—Folder to hold source files, often used for CoffeeScript files
• test—Folder to hold tests for the module
If you have downloaded the code examples for this book, the folder
structure is available as hour22/example02.
Did You Know?: .npmignore and .gitignore Are Complementary
If you want to keep files out of the npm registry, add them to the .npmignore
file. If a module has no .npmignore file, but does have a .gitignore, then npm
uses the contents of the .gitignore file for what to ignore in the registry. If
you want to exclude some files from Git but not from the npm registry, use
both a .gitignore file and a .npmignore file.
npm link
/usr/local/lib/node_modules/ohaithere ->
/Users/george/code/nodejsbook.io.examples/hour22/example03
This creates a global link for your module on your computer so you
are now able to start Node from the terminal and require your
module just as would any other that you have installed on your
system. The module that you develop in this hour is simple. It has a
single function of hello() that returns a string saying Hello from the
ohaithere module.
Now that the module is linked, you can create the module:
1. If you are following the suggested convention, add a new file into
the lib folder and give the file the same name as your module. In this
case, a file called ohaithere.js is added to the lib folder.
2. Once the new file has been added, you can amend the
package.json file to note the entry point for the module:
"main" : "./lib/ohaithere.js"
3. Building on the Test Driven Development (TDD) approach that
you learned in Hour 10, “Testing Node.js Applications,” you first
write a test for this functionality and then write some code to make
the test pass. A new folder called test is created to hold tests for the
module. For the test, you want to test that the hello() function
returns a certain string. Using Node.js’s native assert module, the
test looks like this:
var assert = require ('assert'),
ohaithere = require('../lib/ohaithere.js');
/**
* Test that hello() returns the correct string
*/
assert.equal(
ohaithere.hello(),
'Hello from the ohaithere module',
'Expected "Hello from the ohaithere module". Got "' +
ohaithere.hello() + '"'
)
4. Copy this example and add it to the test folder as ohaithere.js.
Note that after the assert module, the main file for the module that
you are developing is included.
5. The package.json file is able to register how tests should be run on
the application. This enables tests to be run by the npm
test command. To register how to run tests for the module, the
following is added to the package.json file:
"scripts": {
"test": "node ./test/ohaithere.js"
}
6. When this is complete, you can run the tests with npm test:
npm test
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: Object #<Object> has no method 'hello'
7. You see that the test fails, as you have not yet implemented
the hello method. To do that, add the following to the
lib/ohaithere.js file:
exports.hello = function() {
var message = "Hello from the ohaithere module";
return message;
};
8. If you run the tests again, you should see that the tests pass!
Note that the word “exports” is used. This is used to expose the
function to the external or public scope of the module, making it
accessible to anyone who wants to use the module. This is the
pattern to use if you have a series of functions that you want to make
public to users of your module. If you have private functions that you
only want to use within your module, then just declare them as
normal functions. They will not be accessible outside your module. If
you are programming in an object-oriented or prototype-based style,
making parts of your module public is slightly different. This is
covered shortly.
Try It Yourself
If you have downloaded the code examples for this book, the code is
hour22/example03.
The following steps demonstrate how to develop a module using a test-
driven approach:
1. Building on Example 1 where you created a package.json file, create lib
and test folders alongside the package.json file.
2. Within the lib folder, create an empty file called ohaithere.js.
3. Add a main declaration to your package.json file to read as follows:
"main" : "./lib/ohaithere.js"
4. Within the test folder, create a file called ohaithere.js and add the
following content:
var assert = require ('assert'),
ohaithere = require('../lib/ohaithere.js');
/**
* Test that hello() returns the correct string
*/
assert.equal(
ohaithere.hello(),
'Hello from the ohaithere module',
'Expected "Hello from the ohaithere module". Got "' + ohaithere.hello() + '"'
)
5. Add the following to the package.json file to let npm know where to find
tests:
"scripts": {
"test": "node ./test/ohaithere.js"
}
6. Run the tests from the root folder of your module by running the
following command in the terminal. You should see that the tests fail:
npm test
7. Open the lib/ohaithere.js file and add the following code:
exports.hello = function() {
var message = "Hello from the ohaithere module";
return message;
};
8. Run the tests again. You should see that they pass.
An example of using Mocha to test modules is also available as
hour22/example04.
ADDING AN EXECUTABLE
Executables are commands that you can run directly from the
terminal. For example, the command npm is an executable. If you are
following the suggested convention, executable files are added to a
bin folder within a module. In this case, a file called ohaithere.js is
created to call the hello() function and log the output to the console:
#!/usr/bin/env node
var ohaithere = require("../lib/ohaithere");
console.log (ohaithere.hello());
This tells npm that there is an ohaithere executable and that it can be
found at ./bin/ohaithere.js. If the executable is the same name as
your module, the syntax is shorter because you can leave out the
name of the command:
"bin": "./bin/ohaithere.js"
After adding these files, you must run npm link again to link the new
executable into your system:
npm link
/usr/local/bin/ohaithere ->
/usr/local/lib/node_modules/ohaithere/bin/ohaithere.js
/usr/local/lib/node_modules/ohaithere ->
/Users/george/code/nodejsbook.io.examples/hour22/example05
Now, you can run the ohaithere command from anywhere on your
system and see the output in your terminal!
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour22/example05.
Follow these steps to add an executable to a module:
1. Building on Example 3, add a folder called bin to the module.
2. Within the bin folder, create a file called ohaithere.js and add the
following content:
#!/usr/bin/env node
var ohaithere = require("../lib/ohaithere");
console.log (ohaithere.hello());
3. Amend the package.json file to include a declaration for the executable:
"bin": "./bin/ohaithere.js"
4. Run npm link from the root folder of your module to link the executable
into your system:
npm link
5. From the terminal, run the ohaithere command:
ohaithere
6. You should see “Hello from the ohaithere module.”
function Ohaithere(){}
Ohaithere.prototype.hello = function(){
var message = "Hello from the ohaithere module";
return message;
};
function Ohaithere(){
this.hello = function(){
var message = "Hello from the ohaithere module";
return message;
};
}
Often, any of these techniques can be used in parallel. The rule here
is that if you use an object-oriented style of JavaScript
programming, you must use module.exports to export your object and
use programmatic techniques to make methods private or public. If
you are just using a functional style of programming, you can
use exports to make a method public or declare a function
without exportsto make it private.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour22/example06. There is also an example of using privileged methods
available as hour22/example07.
The following steps show you how to write modules with a prototypal style:
1. Building on Example 5, open the lib/ohaithere.js file and change the
content to use a prototypal-based programming style.
module.exports = new Ohaithere;
function Ohaithere(){}
Ohaithere.prototype.hello = function(){
var message = "Hello from the ohaithere module";
return message;
};
2. From the root of your module, run the test tests. You should see that they
pass.
"repository": {
"type": "git",
"url": "https://github.com/yourusername/yourproject.git"
}
If you are using GitHub to track bugs and issues, you may also
specify this in the package.json file. Many larger projects have
mailing lists, and this can also be included within the bugs section:
"bugs": {
"email": "[email protected]",
"url": "http://github.com/shapeshed/ohaithere/issues"
}
USING TRAVIS CI
A popular tool within the Node.js community is Travis CI
(http://travis-ci.org/). This is a free cloud-based, distributed
Continuous Integration server. In this context, Continuous
Integration means that your tests are run every time there is a
change to your codebase. So, every time you push into your GitHub
repository, Travis CI runs your tests and can report to you if there is
a problem. This can increase the stability of your code, as if there are
any issues with your tests, you are certain to know about it.
To use Travis CI, you need the following:
• A GitHub account (you can sign up for free at http://github.com).
• Your source code must be in a GitHub repository.
To begin using Travis CI, you must sign into the Travis CI site with
your GitHub details (see Figure 22.2). Once this is complete, follow
the link to your profile in the top right-hand corner.
You see your list of GitHub repositories (see Figure 22.3). To use a
repository with Travis CI, you must first turn it on by flicking the
switch.
The final step is to create a file within your project that tells Travis
CI what to test and how to run the tests within your project. This is
a .yml (Yet Another Markup Language) file that declares that the
code should be tested against versions 0.4, 0.6, and 0.7 of Node.js:
language: node_js
node_js:
- 0.4
- 0.6
- 0.7
Save this file to the root of your project as .travis.yml. Now when you
push the update to GitHub, your tests will be run on Travis CI. An
excellent feature of Travis CI is that you can show the status of the
Continuous Integration test on your README page by adding the
following snippet of markdown:
[](http://travis-
ci.org/yourgithubuser/yourproject)
PUBLISHING TO NPM
To date, you have done a lot of work on your module! You have
• Created a package.json file
• Created tests for how the module should work
• Added code to make the module work how you expect it to
• Added an executable to the module
• Added the project to GitHub
• Hooked the project up to Travis CI for Continuous Integration
testing
The last thing to do is to publish your module to the npm registry.
The way this works is that your code is compressed into a tarball and
sent to the npm registry servers where it is stored so that it may be
installed and used by other developers (see Figure 22.6). To publish
to the registry, you must have an account. To create an account, run
the following command:
npm adduser
npm publish
npm publish
http.Server(app).listen(3000);
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour23/example01.
A basic middleware example is shown in the following steps:
1. Create a file called app.js and copy the following code into it:
var connect = require('connect'),
http = require('http');
http.Server(app).listen(3000);
2. Create a package.json file with the following content:
{
"name": "middleware",
"version": "0.0.0",
"dependencies": {
"connect" : "2.0.1"
}
}
3. Install the dependencies:
npm install
4. Start the server:
node app.js
5. Open a web browser at http://0.0.0.0:3000.
6. You should see Hi! Hello World in your browser.
At this point, there is nothing different from the functionality
available with Node’s HTTP module. But, wrapping the HTTP
module with Connect enables Middleware to be added to the
standard HTTP module.
Note that in the example, Node’s HTTP module is being used and
that the Connect application is passed into http.Server. The Hello
World response is actually a piece of Middleware itself, too.
Middleware is added to the Connect application by mounting it
within the Connect server. Middleware can be mounted for the
entire application or just specific routes and is chained together in
the order that it is declared. In the following example, Middleware is
mounted in the Connect application and is used to respond to all
requests:
http.Server(app).listen(3000);
http.Server(app).listen(3000);
Now that you understand a little more of what Middleware is, you
can begin to think of potential uses for Middleware. Although
Connect is a Middleware framework, it ships with some useful
Middleware for commonly used scenarios that can give you a good
insight into what Middleware is good for. These include
• A logger for logging information to a file
• Basic authentication to provide username and password protection
to a page or site
• A cookie parser
• Cross-site request forgery protection
These are all good examples of Middleware where the requirement is
to do something with a request or response that is independent of
your application code. As Middleware can be encapsulated in a
Node.js module, this can make your application more modular and
maintainable. The following is an example from Connect’s source
code and is the Middleware for converting HTTP requests from one
sort to another. This is often used for making PUT requests from
forms in a web browser. Forms in most web browsers only support
making GET or POST requests, but many APIs expect a PUT
request. This is a good example of how Middleware can be used to
manipulate the request before it hits the application code:
// req.body
if (req.body && key in req.body) {
req.method = req.body[key].toUpperCase();
delete req.body[key];
// check X-HTTP-Method-Override
} else if (req.headers['x-http-method-override']) {
req.method = req.headers['x-http-method-
override'].toUpperCase();
}
next();
};
};
function helloWorld(req,res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hi! We are open!');
}
http.Server(app).listen(3000);
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour23/example03.
Follow these steps to use access control with Middleware:
1. Create a file called app.js and copy the following code into it:
var connect = require('connect'),
http = require('http');
function helloWorld(req,res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hi! We are open!');
}
http.Server(app).listen(3000);
2. Create a package.json file with the following content:
{
"name": "middleware",
"version": "0.0.0",
"dependencies": {
"connect" : "2.0.1"
}
}
3. Install the dependencies:
npm install
4. Start the server:
node app.js
5. Open a browser at http://0.0.0.0:3000.
6. If you are viewing the page between 9 in the morning and 5 at night, you
should see “Hi! We are open!” (see Figure 23.3). If not, you should see “We
are closed. Come back between 0900 and 1700.”
Figure 23.3. Time-based access with Middleware
7. Try stopping the server, amending the hours, restarting the server, and
opening the page again so you can see how it works.
Limiting Access by IP Address
When clients make requests to a Node.js server, it is possible to find
the IP address that the request is originating from
in req.connection.remoteAddress, where req is the request object. This
can be used to create some Middleware that filters access to an
application based on the originating IP address of the request. In
this example, you see how to make Middleware more flexible by
allowing arguments to be passed in:
function filterByIp(ips){
var ips = ips || [];
return function (req, res, next){
if (ips.indexOf(req.connection.remoteAddress) == -1) {
res.writeHead(401, {'Content-Type': 'text/plain'});
res.write('Sorry. You are not allowed to access this server');
res.end();
} else {
next();
}
};
};
function filterByIp(ips){
var ips = ips || [];
return function (req, res, next){
if (ips.indexOf(req.connection.remoteAddress) == -1) {
res.writeHead(401, {'Content-Type': 'text/plain'});
res.write('Sorry. You are not allowed to access this server');
res.end();
} else {
next();
}
};
};
function helloWorld(req,res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('You can view this!');
}
http.Server(app).listen(3000);
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour23/example04.
Follow these steps to use Middleware to filter by IP addresses:
1. Create a file called app.js and copy the following code into it:
var connect = require('connect'),
http = require('http');
function filterByIp(ips){
var ips = ips || [];
return function (req, res, next){
if (ips.indexOf(req.connection.remoteAddress) == -1) {
res.writeHead(401, {'Content-Type': 'text/plain'});
res.write('Sorry. You are not allowed to access this server');
res.end();
} else {
next();
}
};
};
function helloWorld(req,res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('You can view this!');
}
http.Server(app).listen(3000);
2. Create a package.json file with the following content:
{
"name": "middleware",
"version": "0.0.0",
"dependencies": {
"connect" : "2.0.1"
}
}
3. Install the dependencies:
npm install
4. Start the server:
node app.js
5. Open a browser at http://0.0.0.0:3000.
6. You should see that you can access the server.
7. Stop the server and change the IP address on line five of the script to
127.0.0.2.
8. Start the server again:
node app.js
9. Open a browser at http://0.0.0.0:3000.
10. You should see that you can no longer access the server (see Figure 23.4).
Figure 23.4. IP-based access with Middleware
function forceDomain(domain){
var domain = domain || false;
return function (req, res, next){
if (domain && (req.headers.host != domain)){
res.writeHead(301, {"Location": 'http://' + domain + req.url});
res.end();
} else {
next();
}
};
};
function forceDomain(domain){
domain = domain || false;
return function (req, res, next){
if (domain && (req.headers.host != domain)){
res.writeHead(301, {"Location": 'http://' + domain + req.url});
res.end();
} else {
next();
}
};
};
function helloWorld(req,res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World');
}
http.Server(app).listen(3000);
function forceDomain(domain){
domain = domain || false;
return function (req, res, next){
if (domain && (req.headers.host != domain)){
res.writeHead(301, {"Location": 'http://' + domain + req.url});
res.end();
} else {
next();
}
};
};
function helloWorld(req,res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World');
}
http.Server(app).listen(3000);
2. Create a package.json file with the following content:
{
"name": "middleware",
"version": "0.0.0",
"dependencies": {
"connect" : "2.0.1"
}
}
3. Install the dependencies:
npm install
4. Start the server:
node app.js
5. Open a browser at http://0.0.0.0:3000.
6. You should see in your address bar that you are redirected to
http://127.0.0.1:3000.
SUMMARY
In this hour, you were introduced to Middleware. You saw how it is
possible to use Middleware with Connect to manipulate a request
and response without hitting your application code. You learned how
Middleware is a thin layer between the client and your application
that offers a lot of power. You saw how to add headers to a response,
limit access based on time, limit access based on IP address, and
how to force all users onto a single domain. During the course of
these examples, you learned how to pass arguments to Middleware
and how a request can flow through Middleware.
Q&A
Q. Should I use Middleware all the time?
A. Middleware is great for manipulating a request and response. It
should be considered as separate from your application, so where
you are doing things like crunching data or querying a database, this
should not be in Middleware.
Q. Is the order of Middleware important?
A. Yes. A request will flow through Middleware in the order that it is
declared in. As you add more Middleware to your application, you
should be aware of this as it can lead to unexpected results if
Middleware is declared in the wrong order.
Q. Can I use Middleware outside Connect and Express?
A. JSGI is another way of using Middleware with Node.js, and a
module exists to use this with Node.js. For more information,
see https://github.com/persvr/jsgi-node. You can also write
Middleware without a framework, although this is beyond the scope
of this hour.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. What types of things is Middleware useful for?
2. What options are available for ending a piece of Middleware?
3. Is Connect the only way you can use Middleware in Node.js?
Quiz Answers
1. Middleware is useful for things like authentication, logging,
caching exception notifiers, and redirection. It is a thin layer that sits
in front of your application logic that can manipulate the request
and response.
2. You can call next() to pass the request and response objects onto
the next piece of Middleware (or the application logic if it is the last
one). You can use res.end() to return the response.
3. No. Connect makes it easy to create Middleware, but you can also
use JSGI or write your own.
EXERCISES
1. Create a Middleware function for Connect that logs the browser
version to the console. Hint: This is available
through req.headers['user-agent'].
2. Read the source code of the Middleware that ships with the
Connect library
at https://github.com/senchalabs/connect/tree/master/lib/middle
ware. Try to understand how a request is being manipulated.
3. Explore the Middleware that is listed on the Node.js Wiki
at https://github.com/joyent/node/wiki/modules#wiki-
middleware. Try to understand what some of these modules are
doing and read the source code of one or more projects.
Hour 24. Using Node.js with Backbone.js
{
"name":"backbone_example",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.8",
"mongojs":"0.4.3",
"jade":"0.22.0"
}
}
app.listen(3000);
!!! 5
html
head
title Node.js / Backbone.js Example
link(rel='stylesheet', href='/stylesheets/style.css')
body
h1 Tasks
div#tasks
script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jqu
ery.min.js')
script(src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.
3.1/underscore-min.js')
script(src='http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9
.1/backbone-min.js')
script(src='/javascripts/application.js')
App.Task = Backbone.Model.extend({
idAttribute: "_id",
});
App.Tasks = Backbone.Collection.extend({
model: App.Task,
url: '/api/tasks'
});
You will see in the console that Backbone.js handles posting the data
for you (it uses jQuery for this) and posts it to the Node.js API.
Backbone.js also handles the response.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour24/example01.
The following steps show a basic Backbone application:
1. Create an Express project with the following structure:
2. Within the package.json file, add the following content:
{
"name":"backbone_example",
"version":"0.0.1",
"private":true,
"dependencies":{
"express":"2.5.8",
"mongojs":"0.4.3",
"jade":"0.22.0"
}
}
3. Within the app.js file, add the following content:
var express = require('express'),
db = require("mongojs").connect('backbone_tasks', ['tasks']);
app.listen(3000);
4. Within the index.jade file, add the following content:
!!! 5
html
head
title Node.js / Backbone.js Example
link(rel='stylesheet', href='/stylesheets/style.css')
body
h1 Tasks
div#tasks
script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.
js')
script(src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/und
erscore-min.js')
script(src='http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.1/backb
one-min.js')
script(src='/javascripts/application.js')
5. Within the application.js file, add the following content:
var App = {};
App.Task = Backbone.Model.extend({
idAttribute: "_id",
});
App.Tasks = Backbone.Collection.extend({
model: App.Task,
url: '/api/tasks'
});
6. Install the required dependencies:
npm install
7. Start the server by running the following from a terminal:
node app.js
8. Open a browser with a JavaScript console at http://127.0.0.1:3000.
9. Open the JavaScript console and enter the following; then press Return to
run the code:
var tasks = new App.Tasks;
tasks.create({ title: "Test Task" });
10. You should see that Backbone.js sends the data to the server and that a
new task is created (see Figure 24.1).
script(id='task_form', type='text/template')
| <form action='' id='task-form'>
| <fieldset>
| <legend>Add a task</legend>
| <input type='text' name='title' class='task-title' />
| <input type='submit' value='Submit' />
| </fieldset>
| </form>
script(id='task_template', type='text/template')
| <li data-id="<%= task.id %>">
| <%= task.get('title') %>
| </li>
script(id='tasks_template', type='text/template')
| <%= task_form() %>
| <ul>
| <% _.each(tasks, function(task){ %>
| <%= task_template({ task: task }) %>
| <% }); %>
| </ul>
Within the index.jade file, this data can now be made available to
Backbone.js by adding a script tag with the data received from the
server at the end of the file:
script
var tasks = new App.Tasks;
tasks.reset(!{tasks});
App.TasksView = Backbone.View.extend({
el: $("#tasks"),
initialize: function() {
this.task_form = _.template($('#task_form').html());
this.tasks_template = _.template($('#tasks_template').html());
this.task_template = _.template($('#task_template').html());
this.render();
},
render: function() {
$(this.el).html(this.tasks_template({
task_form: this.task_form,
tasks: this.collection.models,
task_template: this.task_template
}));
}
});
App.init = function() {
new App.TasksView({ collection: tasks });
}
Finally, the script tag at the end of the index.jade file can be
amended to call the init function and start the Backbone.js
application:
script
var tasks = new App.Tasks;
tasks.reset(!{tasks});
$(function() {
App.init();
});
App.TasksView = Backbone.View.extend({
el: $("#tasks"),
initialize: function() {
this.task_form = _.template($('#task_form').html());
this.tasks_template = _.template($('#tasks_template').html());
this.task_template = _.template($('#task_template').html());
this.render();
},
render: function() {
$(this.el).html(this.tasks_template({
task_form: this.task_form,
tasks: this.collection.models,
task_template: this.task_template
}));
}
});
App.init = function() {
new App.TasksView({ collection: tasks });
}
4. To give Backbone.js access to the data from the server, add the following
to the end of the index.jade file:
script
var tasks = new App.Tasks;
tasks.reset(!{tasks});
$(function() {
App.init();
});
5. Start the Node.js server:
node app.js
6. Open a browser with a JavaScript console at http://127.0.0.1:3000.
7. You should see any tasks that you added in the previous examples
displayed on the page.
events : {
'submit form' : 'createTask'
},
createTask: function (event) {
event.preventDefault();
var taskTitleInput = $('.task-title');
var taskTitle = taskTitleInput.val();
tasks.create({ title: taskTitle }, {
success: function(task){
$('#tasks ul')
.prepend("<li>" + taskTitle" + "</li>");
taskTitleInput.val('');
}
});
}
The createTask function first captures the submit event and prevents
the form from being submitted. Next, using jQuery selectors, the
value that the user entered is captured, and then a task is created
through Backbone.js using the create method on the Tasks
collection. If this is successful, the task is written to the HTML using
jQuery.
Try It Yourself
If you have downloaded the code examples for this book, this code is
hour04/example03.
Follow these steps to create records through Backbone.js:
1. Building on the previous example, add the following to the TasksView block
in application.js:
events : {
'submit form' : 'createTask'
},
createTask: function (event) {
event.preventDefault();
var taskTitleInput = $('.task-title');
var taskTitle = taskTitleInput.val();
tasks.create({ title: taskTitle }, {
success: function(task){
$('#tasks ul')
.prepend("<li data-id=" + task.id + ">" + taskTitle + " <button>Done!
</button></li>");
taskTitleInput.val('');
}
});
}
2. Run the script:
node app.js
3. Open a browser with a JavaScript console at http://127.0.0.1:3000.
4. Enter a task into the input and click the Submit button.
5. You should see your task is written to the page.
6. Refresh the browser window.
7. You should see the task that you just created is still on the page (see Figure
24.2).
Figure 24.2. Adding records through a Backbone.js view
SUMMARY
In this hour, you learned how to use Node.js as an API for
Backbone.js, a client-side framework for creating rich JavaScript
applications in the browser. You learned how the entire application
stack can be JavaScript with Node.js on the server, MongoDB as the
data store, and Backbone.js on the client side. You were introduced
to Backbone.js and learned how to create a Model, Collection, and a
View and saw how to use Backbone.js to create a basic single-page
application.
Q&A
Q. When should I use a single-page application?
A. A single-page application means that the user never refreshes the
page once the page has loaded. If you need your application to be
searchable and bookmarked by users, a single-page application is
probably not a good choice.
Q. Can Backbone.js use URLs?
A. Backbone.js supports using URLs in applications too and uses the
HTML5 History API to allow the URL in the browser windows to be
updated. For older browsers, Backbone.js gracefully falls back to the
fragment version of the URL.
Q. Is using JavaScript for the entire application a good idea?
A. For many browser-based applications, using JavaScript for the
entire application is a great idea. But as always, it depends on what
your application is trying to do. If you want to create a desktop-type
application for the browser, using JavaScript for the server, data
store, and client is an excellent choice.
Q. Where can I learn more about Backbone.js?
A. The Backbone.js documentation is a good place to start and can
be found at http://documentcloud.github.com/backbone/. A
number of commercial screencasts are available at
PeepCode https://peepcode.com/products/backbone-js and http://
backbonescreencasts.com/.
WORKSHOP
This workshop contains quiz questions and exercises to help cement
your learning in this hour.
Quiz
1. Is Backbone.js just for single-page applications?
2. Is Backbone.js the only framework for creating rich client-side
applications?
3. Do you need to use Node.js as the server for Backbone.js
applications?
Quiz Answers
1. No. Although Backbone.js excels at creating single-page
applications, it provides many features to allow client-side
applications to interact with a server. Depending on your application
and requirements, jQuery or just plain JavaScript may be enough,
though.
2. No. There are many client-side frameworks for creating desktop-
type applications in the browser. Some of these include Spine.js and
Ember.js. A good comparison of client-side frameworks can be
found at http://codebrief.com/2012/01/the-top-10-javascript-mvc-
frameworks-reviewed/.
3. No. Backbone.js is agnostic to the server application. Node.js
makes a great choice, though!
EXERCISES
1. Add some validation to the Node.js API so that a task cannot be
submitted without a title.
2. Write some tests for the Node.js API to ensure that Backbone.js
can interact with the API as expected.
3. Add the capability to delete a task. The solution is available in the
book’s code examples as hour24/example04.