Node Tutorial Part 7

27 Dec 2010 | By Alex Young | Tags server node tutorials lmawa nodepad npm express

Welcome to part 7 of Let’s Make a Web App, a tutorial series about building a web app with Node. This series will walk you through the major areas you’ll need to face when building your own applications. These tutorials are tagged with lmawa.

Previous tutorials:

Package Versions

I’ve updated the Nodepad README to include the versions of Node and Mongo that I’m using. It also includes the versions of the packages I’ve used. That should help you actually get the code running if it doesn’t seem to work. I’ve tested it on Mac OS and Debian.

Also remember that you need to restart Node whenever you change code (but not Jade templates).

We’re using npm to install packages, and it sets up path names so specific package versions can be required. To install a package at a specific version, do this:

npm install express@1.0.0

Then to use it, do this:

var express = require('express@1.0.0');

You can verify this works by typing node and entering the previous line:

> express = require('express@1.0.0')
{ version: '1.0.0'
, Server: { [Function: Server] parseQueryString: [Function] }
, createServer: [Function]
}

Jade Tricks

When I first demonstrated Jade I hardcoded all the attributes. It’s possible to save a lot of effort by writing selectors as a shorthand for classes and IDs:

div#left.outline-view
  div#DocumentTitles
    ul#document-list
      - for (var d in documents)
        li
          a(id='document-title-' + documents[d].id, href='/documents/' + documents[d].id)
            =documents[d].title

Notice that an ID selector has been combined with a class name: div#left.outline-view.

The default tag is div, which means the previous example can be reduced to this:

#left.outline-view
  #DocumentTitles
    ul#document-list
      - for (var d in documents)
        li
          a(id='document-title-' + documents[d].id, href='/documents/' + documents[d].id)
            =documents[d].title

Error Pages

Express allows us to define a custom error handler with app.error:

// Error handling
function NotFound(msg) {
  this.name = 'NotFound';
  Error.call(this, msg);
  Error.captureStackTrace(this, arguments.callee);
}

sys.inherits(NotFound, Error);

// This method will result in 500.jade being rendered
app.get('/bad', function(req, res) {
  unknownMethod();
});

app.error(function(err, req, res, next) {
  if (err instanceof NotFound) {
    res.render('404.jade', { status: 404 });
  } else {
    next(err);
  }
});

app.error(function(err, req, res) {
  res.render('500.jade', {
    status: 500,
    locals: {
      error: err
    } 
  });
});

Error handlers get four parameters, the error, req, res, and next. The next method can be used to pass the error on to the next handler. In the previous example, the 404 handler passes on errors that aren’t NotFound, and we effectively catch all other errors and consider them 500s.

Visiting /bad in the browser will display the custom 500 page. Notice that I specify the HTTP status code in the options for render — it’s important to correctly specify status codes else a 200 will be returned instead of a 404 or 500.

Error Handling within Mongoose Code

The next method is available to all of our app HTTP verb methods, which means we can use it to render a custom 404 page:

app.get('/documents/:id.:format?/edit', loadUser, function(req, res, next) {
  Document.findById(req.params.id, function(d) {
    if (!d) return next(new NotFound('Document not found'));
    // Else render the template...

This is the pattern that I found easiest to read when using Mongoose. Simply using throw new NotFound within a Mongoose callback will cause the application to crash rather than Express’s error handlers being triggered.

Conclusion

When distributing or deploying Node applications it’s important to be specific about package versions. Many key packages are still under heavy development, so distribution in particular can be difficult.

Express makes it fairly easy to create custom error handlers with templates, but make sure you specify the HTTP status code and use next(exception) when in a callback.

This version of Nodepad was in commit 929f564.


blog comments powered by Disqus