Express is a popular Node module that simplifies the development of Web applications. It is comparable to Ruby's Sinatra framework and Python's Flask framework in that it makes it simple to respond to HTTP requests at defined endpoints and serve up responses, typically HTML generated by a view engine or JSON data.

Getting started with Express

Most guides to Express start off by installing express-generator, an npm module that generates a boilerplate Express app, complete with basic error handling, sample routes and views. Rather than do the same, let's see how to build an Express app from scratch.

Create a new directory for this project called expressapp. Navigate to this directory in your terminal and run the following command to create a package.json file.

$ npm init

Next, install express with the following command. The --save flag is passed to instruct npm to include express as a dependency in the package.json file. This will allow it to be installed in future by running npm install.

$ npm install --save express

When the command has finished, Express will be installed in your project's node_modules subdirectory, and npm will output a list of its dependencies to the terminal. Next, let's build our first Express app. Create a new file in your project directory named app.js, and add the code below to it.

app.js source code

var express = require('express');
var app = express();

app.get('/', function(req, res) {
  res.send('Hello, world!');
});

var server = app.listen(3000, function() {
  console.log('Express app running on port %s', server.address().port);
});

Run the app using the following command:

$ node app.js

You should see the following message:

Express app running on port 3000

Open your preferred Web browser and navigate to http://localhost:3000. You should see the message Hello, world! displayed on the page. Congratulations, you've just created your first Express app! Let's explore the code to figure out what exactly is going on.

var express = require('express');
var app = express();

First, we load the express module into our app, and we execute it, assigning the result to the app variable.

app.get('/', function(req, res) {
  res.send('Hello, world!');
});

We then use the get method to define a route. This method accepts two parameters - the URI endpoint for the route, in this case the root of the app or /; and a callback function which will be executed when someone accesses this route. Within the callback, we have the req variable which is the request object, and the res variable which is a writable stream containing the response that is returned to the user.

In our app, we are using the send method on the response object to send the message Hello, world! to the user.

var server = app.listen(3000, function() {
  console.log('Express app running on port %s', server.address().port);
});

Finally, we use the listen method to tell Node that we want to listen for incoming connections to our app. The first argument to this method is the port number to listen on, in this case 3000; and the second argument is a callback function that will be executed when completed. Here, we are logging a message to the terminal to let us know that the app is running on port 3000.

The server will continue to run until you explicitly stop it, typically using Ctrl+C in your terminal window. You'll learn how to run Node apps in the background and how to ensure they stay alive in a future post.

Routing

In the previous section, you created a simple app that listens for HTTP GET requests and returns a response. It is just as simple to listen for requests made using other HTTP methods. For example, to listen for a POST request, you would write:

app.post('/person', function(req, res) {
  //Do something
});

The same applies for other HTTP methods, including PUT and DELETE. You can also write code that handles all HTTP methods, that hands down control to a subsequent handler once it's finished. This is particularly useful if you have code that should run regardless of the HTTP method, but also want to do something specific for individual request methods.

Handling multiple HTTP methods

app.all('/person', function(req, res, next) {
  console.log('Somebody made a %s request', req.method);
  next();
});

app.get('/person', function(req, res) {  
  res.send('Hello from the GET handler!');
});

app.post('/person', function(req, res) {
  res.send('Hello from the POST handler!');  
});

In this example, a message will be logged to the terminal regardless of what request method was used. Then, control will be passed down to the next handler for the request using the next method.

Tip: Testing GET requests is easy, you simply point to a URL with your Web browser and the response will be displayed. To make testing POST (and other methods) requests easy, check out the Postman Chrome extension.

Another approach to defining handlers for multiple HTTP methods is to to use the route method. This allows you to chain handlers in a single statement. The main benefit of this approach is that it saves you from duplicating the endpoint on each method. The following listing illustrates the previous example, refactored to use the route method.

Using the route method to chain HTTP method handlers

app.route('/person')
  .all(function(req, res, next) {
    console.log('Somebody made a %s request', req.method);
    next();
  })
  .get(function(req, res) {  
    res.send('Hello from the GET handler!');
  })
  .post(function(req, res) {
    res.send('Hello from the POST handler!');  
  });

Express routes enable you to define placeholders in the route that map to parameters in the request. This is particularly useful for building a RESTful API, where you accept resource identifiers in the URI.

Accepting parameters in the URI

app.get('/person/:id', function(req, res) {
  res.send('Return the resource with the ID ' + req.params.id);
});

In this example, if you navigate to http://localhost:3000/person/1234 you will see the message Return the resource with the ID 1234. You could easily take this ID and use it to search for a record in a database and return the relevant data as JSON.

As your Express apps become larger, it can be difficult to maintain many routes in a single file. Thankfully, Express includes a class named express.Router that alleviates this problem. This enables you to create modules that define a series of routes. You can then load these modules in your main app and bind the routes within the module to a specific endpoint.

Create a new file named person.js and save it in the same directory as app.js. Add the following code to this new file.

person.js source code

var express = require('express');
var router = express.Router();

router.get('/', function(req, res) {
  res.send('Person list');
});

router.get('/new', function(req, res) {
  res.send('Add new person');
});

module.exports = router;

Next, change the app.js file so that its contents match the code below.

app.js source code

var express = require('express');
var person = require('./person');

var app = express();

app.use('/person', person);

var server = app.listen(3000, function() {
  console.log('Express app running on port %s', server.address().port);  
});

Run the app again using $ node app.js. You should be able to navigate to the URLs http://localhost:3000/person and http://localhost:3000/person/new and see the responses Person list and Add new person, respectively.

2.3. JSON Responses

We've already seen how we can use the send method on the response object to return a string of text to the user when they access a route. The response object has many other options, however.

Express is often used to build APIs. It is common practice in modern APIs to return data in the JavaScript Object Notation (JSON) data interchange format. Express makes it very easy to return JSON from your routes. You simple use the json method on the response object to take a JavaScript object or array and return it as JSON. Let's tweak the previous example so that the route that previously returned the text Person list now returns JSON representing an array of person objects.

Returning a JSON response

router.get('/', function(req, res) {
  var persons = [
    {firstName: "Joe", lastName: "Lennon"},
    {firstName: "Jill", lastName: "Lennon"}
  ];
  res.json(persons);
});

When you run the app again and navigate to http://localhost:3000/person, you will see a response similar to the following:

[{"firstName":"Joe","lastName":"Lennon"},{"firstName":"Jill","lastName":"Lennon"}]

Warning: Whenever you make changes to the code behind an Express app, you will need to kill the program and run it again to see the changes you have made take effect.

Express automatically encodes the array as JSON and sends the response with the appropriate headers (i.e. it sets the Content-Type to application/json).

Express also makes it easy to support requests with JSONP callbacks via the jsonp method of the response object. This works exactly the same way as the json method, except that it looks for the name of a callback function in the request's query string, and passes the JSON data to this callback function in the response. If you change the previous example to use jsonp, and then navigate to http://localhost:3000/person?callback=list, you will see the following response:

/**/ typeof list === 'function' && list([{"firstName":"Joe","lastName":"Lennon"},{"firstName":"Jill","lastName":"Lennon"}]);

This makes it easy to support cross-domain API requests without running into Cross-Origin Resource Sharing (CORS) errors.

File Downloads

Express also makes it easy to serve up files that can be downloaded by the user. There are two methods available - sendFile and download. The primary difference between these two methods is that the download method will set a Content-Disposition header on the response, which will force most Web browsers to initiate a file download, whereas files served with sendFile will typically be opened by an application associated with the file type if available rather than explicitly downloaded.

The response object in Express is a writable stream, which means that you can pipe a series of readable streams to it to perform file manipulation and send the results in chunks. This allows you to perform intensive processes on large files without causing undue stress on your server.

Let's look at an example of this, where we will read JSON data from a file, transform it to CSV and then send this as a file download to the user with Express. To get this working, we will need to use a few additional third-party Node modules - JSONStream, sprintf and event-stream. Install these with the following command:

$ npm install --save JSONStream sprintf event-stream

Next, create a new file named person.json and add the following code to it:

person.json source code

[{"firstName":"Joe","lastName":"Lennon"},{"firstName":"Jill","lastName":"Lennon"}]

Next, open the person.js file we created earlier in this post and change it so that its contents match the following code.

person.js source code

var express = require('express'),
    fs = require('fs'),
    JSONStream = require('JSONStream'),
    sprintf = require('sprintf'),
    es = require('event-stream');

var router = express.Router();

router.get('/', function(req, res) {
  res.attachment('person.csv');
  fs.createReadStream('person.json')
    .pipe(JSONStream.parse('*'))
    .pipe(es.mapSync(function(data) {
      return sprintf.sprintf('%(firstName)s,%(lastName)s', data);
    }))
    .pipe(es.join('\n'))
    .pipe(es.wait())
    .pipe(es.mapSync(function(data) {
      return 'FirstName,LastName\n' + data;
    }))
    .pipe(res);
});

module.exports = router;

Run your app again with $ node app.js and navigate to http://localhost:3000/person. A file named person.csv will be downloaded by your browser, and if you open it, you should see the following contents:

FirstName,LastName
Joe,Lennon
Jill,Lennon

There's quite a bit of new code in this example, so let's take a look at it.

var express = require('express'),
    fs = require('fs'),
    JSONStream = require('JSONStream'),
    sprintf = require('sprintf'),
    es = require('event-stream');

Here we are simply loading the newly installed modules JSONStream, sprintf and event-stream.

res.attachment('person.csv');

This uses the attachment method of the response object to set the Content-Disposition response header, setting the filename to person.csv.

fs.createReadStream('person.json')
  .pipe(JSONStream.parse('*'))

This code creates a readable stream for the person.json file and pipes it through to the parse method of the JSONStream module. This will take the JSON data and parse it into JavaScript.

.pipe(es.mapSync(function(data) {
  return sprintf.sprintf('%(firstName)s,%(lastName)s', data);
}))
.pipe(es.join('\n'))
.pipe(es.wait())
.pipe(es.mapSync(function(data) {
  return 'FirstName,LastName\n' + data;
}))

It might look like there's a lot going on here, but it's actually pretty simple. This code uses the the mapSync method of the event-stream module along with the sprintf module to convert JavaScript objects into CSV lines. This is then piped into the join method, which will join the CSV lines using the \n newline character. The wait method will wait until all the chunks of the stream have been processed and concatenate the chunks together. Finally, we wrap the entire thing in a mapSync that adds the CSV header line.

.pipe(res);

Earlier I mentioned that Express response objects are themselves streams - here we simply pipe the stream to the response object. The data from the stream will be sent to the response, which will be served as a file download due to the attachment call we made earlier.

Rendering view templates

At this point we have seen how to return responses that are plain text, JSON data and even file downloads. But for many applications, the bulk of the responses you will want to send will be HTML pages. You could construct a HTML response directly in your Express routes, but I don't need to tell you how bad that would be.

Express supports a number of view engines, the most popular of which is Jade. Jade is a short-hand syntax for HTML that relies on indentation for document structure. It is a decent template language with support for conditionals, iteration, extendable layouts and mixins.

To use Jade as the Express view engine, you first need to install it.

$ npm install --save jade

Next, you need to tell Express that you want to use Jade, and where to look for views when rendering templates. Open the app.js file and below the line var app = express(), add the following lines:

var jade = require('jade');
app.set('views', './views');
app.set('view engine', 'jade');

Next, let's create a simple Jade template. Create a new subdirectory named views, and in it create a new file named persons.jade. Add the code below to this file.

views/persons.jade source code

html
  head
    title Persons
  body
    table
      thead
        tr
          th First Name
          th Last Name
      tbody
        for person in persons
          tr
            td= person.firstName
            td= person.lastName

If you have never seen a template language like Jade before, it may look a bit strange at first. Essentially it's HTML without the angle brackets and closing tags, relying on indentation for structure. The most interesting lines in this example are the following:

for person in persons
  tr
    td= person.firstName
    td= person.lastName

This will iterate over an array of objects named persons, and for each object in the array it will create a new table row with two cells that output the firstName and lastName properties of the object. Note that to output a value you use the = symbol after the element name.

We will explore Jade in much more detail in a future post.

Next, we need to change our route to use this template. Open person.js in your project's root directory. Change the contents of this file to the code below.

person.js source code

var express = require('express'),
    fs = require('fs');

var router = express.Router();

router.get('/', function(req, res) {
  fs.readFile('person.json', function(err, data) {
    var json = JSON.parse(data);

    res.render('persons', { persons: json });
  });  
});

module.exports = router;

If you start your app with $ node app.js and navigate to http://localhost:3000/person, you should see the table in all its glory.

Static content

Template languages like Jade are great for generating dynamic content, but what about serving up static content such as images, stylesheets and client-side scripts? Simply create a new subdirectory (typically named public or similar) and add your static files here. To tell Express to serve up the contents of this directory as static files, add a line to your app.js file, below the app.use statements you added in the previous section.

app.use(express.static('public'));

This will serve any files in the public directory as static content in your app. Using the declaration above will serve these files from the root of your app, i.e. /. For example, if you have the following files:

  • public/images/profile.jpg
  • public/scripts/app.js
  • public/styles/app.css

These will be served at the following locations, respectively:

If you'd prefer to add a prefix to the URI for static content, change the line you added to app.js as follows:

app.use('/public', express.static('public'));

The files will now be served at the following locations:

Which approach to use is a matter of personal preference. I prefer to use the explicit approach so that I don't run into any confusion down the line due to naming conflicts (where I have a view and a static resource with the same name).

Middleware

Express is entirely built on the concept of middleware - pieces of code that have access to the request object, the response object and the next middleware in the chain. Middleware makes it simple to define code that will be run for many requests, for example to perform validation such as verifying that a user is authenticated. Express middleware can change the request and response objects, send a response or pass on to the next middleware in the stack.

All of the router methods you learned about earlier in this post (i.e. get, post, etc.) are middleware methods. There is another method, use, that can handle any HTTP method, and can even run without a route. This allows you to inject common functionality across your app, such as logging, that will execute on every request that is made.

For example, the following code will write a message to the terminal window with a timestamp, URL, request method and IP address every time anyone visits any URI in the app.

app.use(function(req, res, next) {
  var dateTime = new Date().toJSON().slice(0, -5);
  console.log('[%s] %s %s %s', dateTime, req.url, req.method, req.ip);  
  next();
});

Another common use for middleware is to include additional functionality in your Express app. The express.static method used in the previous example is an example of this type of middleware. In fact, this is the only middleware that is included with Express out-of-the-box - all other middleware must be included from third-party modules.

There are some common middleware options that will prove useful in most applications. For example, body-parser is essential for parsing request bodies that are sent as JSON or form-encoded data. If your Express app includes a HTML form, you'll want to use the body-parser middleware. So let's include that now.

First, install the module from npm.

$ npm install --save body-parser

Next, in your app.js file, below the line var app = express(); add the following line:

var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

Some other popular Express middleware include:

Error handling

Like routing, error handling in Express is simply an implementation of middleware. The only difference with error handling middleware is that it accepts four arguments instead of three. You should put error handling middleware after all other middleware and routes. An example of an error handler is as follows:

app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Unexpected server error occurred!');
});

This will log the details of the error to the console, and will send a response to the user with a 500 error status code and an error message. You can put whatever code you like in here - you can render a view, add errors to a log, send an email, etc.

This error handler will only capture application errors - it won't catch typical 404 "Page Not Found" errors. To handle 404 errors, simply add a regular middleware at the end of your app.js file - this will be executed only if no other middleware or route has already handled the request - indicating that no resource has been found.

app.use(function(req, res, next) {  
  res.status(404).send('Not Found!');
});

Finally, if your application encounters an error you can return this from your code so all subsequent non-error handling middleware will be ignored, for example:

app.get('/update', function(req, res, next) {
  var err = validateRequest(req);
  if(err) return next(err);
  res.send('Success');
});

You can view the internal logs used by Express by setting the DEBUGenvironment variable to express:* when starting your app.

$ DEBUG=express:* node app.js

Express uses the debug module for logging events. This module can also be very useful for your own application logging purposes, so it's worth checking out.

Summary

Express is a hugely popular module for Node, and it's easy to see why. It is basic enough to provide a high level of flexibility for your project's needs, but powerful enough to handle the low-level complexity that most projects prefer to ignore. In a future post, I'll take a closer look at the Jade template engine.