Chapter 11: Transactions on the Web
11.1 Setting Up Your Own Web Server: Node.js
Node.js is a stand-alone JavaScript environment that comes with a Web server library and a modular structure that allows you to add extensions to create a full-featured Web server with just the features you need. It is an ideal way to set up a transactional Web site without having to learn a new programming language.
Documentation for Node.js includes an API reference.
Installing Node.js
Using the Node Package Manager
Basic Node.js
Installing Node.js
Just as writing browser-based JavaScripts still involves a lot of testing, when developing server-side Web applications it’s best to set up your own instance of Node for testing, before publishing the result on your production server.
You can download the Node installer here. Follow the instructions provided for installation.
To test your installation, start a command-line interpreter or “shell” and then open Node in its Read-Evaluate-Print-Loop (REPL) mode, which is similar to your Web browser’s console:
Macintosh:
- Locate Terminal.app in the folder Applications > Utilities, and open it.
- In the Terminal window, at the command-line prompt that ends with your username and
$
, typenode
and then press the keyreturn
.
Windows:
- Start Node’s version of the Windows cmd by menuing Start > All Programs > Node.js > Node.js command prompt.
- In the Node.js command prompt window, at the command-line prompt that ends with your username and
>
, typenode
and then press the keyEnter
.
Interacting with Node is the same in any operating system:
- The Node command prompt
>
will appear; you can now type any JavaScript statement such as1 + 1
, and it will evaluate it and print the result, e.g.2
. - Type
.help
to see a list of REPL-specific commands, which always start with.
. - Type
.exit
to leave Node.
Using the Node Package Manager
Node.js is a very small set of code, and is designed to be modular, so that you can add in just the tools or modules you need for your project. The Node Package Manager (NPM) is installed with Node to facilitate this process. It downloads and installs packages of code from a remote repository to make their modules available to your project.
The NPM Web site has documentation for NPM and information about many of the available packages.
For example, an expected practice for a Web site is to describe the type of file being downloaded in the response header, for example:
Content-Type: text/html
or
Content-Type: image/jpeg
This is a standard format known as Multipurpose Internet Mail Extensions (MIME), developed originally for e-mail attachments. Providing it in the response header will prevent browsers from having to properly interpret a (possibly nonexistent or incorrect) file extension, and is now often required by browsers for security reasons.
Your server can determine MIME types in a number of ways, but if you know for certain that the file extensions you use map to the standard file types, you can use Node’s optional mime
module to look them up. For example,
var filePath = 'index.html';
var mime = require('mime'); // loads MIME-type module
mime.lookup(filePath);
⇒ 'text/html'
To add this module to your Web site:
- In the command-line interpreter, change directory (folder) to where your web pages are (or will be) located. For example, if your Geography of New England documents are on the Desktop in a folder named
gne
, type the command:
You can avoid typing out long pathnames by copying them from the GUI into the CLUI:cd Desktop/gne
- Macintosh: type
cd
(with a space at the end) and then click-and-drag the folder icon from the top of any Finder window into the Terminal window, and its file path will be written as text: - Windows: locate the file path at the top of the folder window in the Windows Explorer and click at the right end of the path, whence it will become text that you can copy, and then return to the Node.js command prompt type
cd
, right-click, and choose Paste:
- Macintosh: type
- Type the following command in your command-line interpreter:
You an ignore the warnings about missing fields. This will install the package in a subfolder namednpm install mime
node_modules
wherenode
can find it.
Basic Node.js
Node comes with a basic Web server library, and a very simple server using Node and the MIME package looks like this:
var http = require('http'); // server module — standard
var url = require('url'); // url-processing module - standard
var fs = require('fs'); // file system module — standard
var mime = require('mime'); // MIME-type module — optional
// Create a web server that, when a page request comes in,
// e.g. "http://localhost:3000/index.html",
// passes it to a callback function to generate a response:
function serverResponse(request, response)
{
var urlPath = url.parse(request.url).pathname; // ignore queries
// Root directory of server files; dot means the same as this file:
var filePath = '.';
if (urlPath == '/') // No explicit document; use default
filePath += '/index.html';
else
filePath += decodeURI(urlPath.replace(/\+/g, ' ')); // decode +, %nn
// Check the file status:
fs.exists(filePath,
function(exists)
{
if (exists)
fs.readFile(filePath,
function(error, file)
{
if (error)
{
response.writeHead(500,
{ 'Content-Type': 'text/plain' });
response.end('Error 500: Internal Server Error');
}
else
{
response.writeHead(200,
{ 'Content-Type': mime.lookup(filePath),
'Content-Length': file.length });
response.end(file);
}
}
);
else
{
response.writeHead(404, { 'Content-Type': 'text/plain' });
response.end('Error 404: Document Not Found.');
}
}
);
}
// Create the web server:
var server = http.createServer(serverResponse);
// Start up the web server:
var port = 3000;
server.listen(port, function() { console.log('Server listening on port ' + port + '.') } );
To try out this script:
- Copy this script and save it in a file named
index.js
in your Web site folder, e.g.gne
. - In the command-line interpreter, make sure you are in in your Web site folder.
- Launch Node with this server:
node index.js ⇒ Server listening on port 3000.
- Then connect to the server in your browser with a reference to an existing file, for example, the address
http://localhost:3000/gne.html
. - Also request a nonexistent document to see the response, e.g
http://localhost:3000/junk.html
. - To stop the server, return to the command-line interpreter and type control-C (hold down the key control or ctrl and press C).
Here is how this script works. Node provides, standard, the basic items necessary for a server, such as the server module, a url-processing module, and a file system module, each loaded with the require()
function, as is the optional MIME-type module:
var http = require('http'); // server module — standard
var url = require('url'); // url-processing module - standard
var fs = require('fs'); // file system module — standard
var mime = require('mime'); // MIME-type module — optional
The server is created with the statement:
server = http.createServer(serverResponse);
where the callback function serverResponse()
is run asynchronously to process document requests whenever they arrive.
The server is launched with the last statement:
var port = 3000;
server.listen(port, function() { console.log('Server listening on port ' + port + '.') } );
which tells the server to listen on port 3000, waiting for document requests to come in. Initially it runs a callback function that logs its status on the Node console — your command-line interpreter, where you should already have seen this message appear.
When requests are made, the callback function serverResponse(request, response)
is provided two arguments, an object containing the request information, and another with information on how to send a response.
- The object
request
provides information about the document, specifically its path relative to the server’s root folder, which is the first folder after server’s name, e.g.http://localhost:3000/
. This path is turned into a file reference by parsing it to remove trailing queries and decoding+
and%nn
encodings in the URL, and then appending it to the location on the server where the documents are stored:var urlPath = url.parse(request.url).pathname; // ignore queries var filePath = '.'; // Root directory of server files; same as this file if (urlPath == '/') // No explicit file; use default filePath += '/index.html'; else filePath += decodeURI(urlPath.replace(/\+/g, ' ')); // decode +, %nn
- The file system function
fs.exists(filePath, function(exists) {....})
tests if the file can be found, and its callback is given the file status as the booleanexists
.- If the file exists, the file system function
fs.readFile(filePath, function(error, file) { .... })
tries to read it, and hands the result to a callback function with another booleanerror
to indicate if it was successful, and if so includes the file’s contents as the stringfile
. This callback function will create a response page one way or another:- If
error
is true and the file cannot be read, an error message is sent, both in the header of the response as HTTP status 500, and as the document itself, sent as the end of the response:response.writeHead(500, { 'Content-Type': 'text/plain' }); response.end('Error 500: Internal Server Error');
- If
error
is false and the file is successfully read, the header is written with the HTTP status code 200 (OK), the MIME type, and the length of the file, followed by the file content as the end of the response:response.writeHead(200, { 'Content-Type': mime.lookup(filePath), 'Content-Length': file.length }); response.end(file);
- If
- If the file does not exist, a different error message is sent, the famous 404 (Not Found):
response.writeHead(404, { 'Content-Type': 'text/plain' }); response.end('Error 404: Document Not Found.');
- If the file exists, the file system function
This kind of error-checking is a typical requirement of most programs you may write, and this doesn’t even cover all of the ways that a document request can fail or otherwise vary (check out the many status codes for HTTP!).
Thankfully you don’t have to write your own servers; the above is just to get you familiar with how they operate. The basic testing has all been written before, and you can take advantage of that knowledge base by installing packages such as Express.