Node Tutorial Part 21: Connection Management

18 Apr 2011 | By Alex Young | Tags server node tutorials lmawa nodepad

Welcome to part 21 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.

Click to show previous tutorials.

Package Updates

I’ve updated all the packages to the latest versions. Express is now on 2.2.2!

HTML Preview

In the Backbone.js tutorials we already added a stub to DocumentControls called showHTML. This is meant to show the HTML preview, which can be obtained by viewing a document with .html appended to the URL. I added a convenience function to the model for getting the right URL:

  Document = Backbone.Model.extend({
    Collection: Documents,

    url: function() {
      return this.urlWithFormat('json');
    },

    urlWithFormat: function(format) {
      return this.get('id') ? '/documents/' + this.get('id') + '.' + format : '/documents.json';
    },

    // ...

Then added the event 'click #html-button': 'showHTML' to DocumentControls and updated showHTML:

showHTML: function(e) {
  var model = this.model;
  e.preventDefault();
  $.get(this.model.urlWithFormat('html'), function(data) {
    console.log($(window).height());
    $('#html-container').html(data);
    $('#html-container').dialog({
      title: model.get('title'),
      autoOpen: true,
      modal: true,
      width: $(window).width() * 0.95,
      height: $(window).height() * 0.90
    });
  });
}

I’m using dialog from jQuery UI to display the contents of the HTML document.

Express Responses

It was possible to make some of my server-side methods hang, like this one:

app.post('/search.:format?', loadUser, function(req, res) {
  Document.find({ user_id: req.currentUser.id, keywords: req.body.s ? req.body.s : null },
                [], { sort: ['title', 'descending'] },
                function(err, documents) {
    switch (req.params.format) {
      case 'json':
        res.send(documents.map(function(d) {
          return { title: d.title, id: d._id };
        }));
      break;
    }
  });
});

This is poor style because an unrecognised format would cause the app to hang. You should avoid this as much as possible when building Express apps and always send something back to the browser:

app.post('/search.:format?', loadUser, function(req, res) {
  Document.find({ user_id: req.currentUser.id, keywords: req.body.s ? req.body.s : null },
                [], { sort: ['title', 'descending'] },
                function(err, documents) {
    switch (req.params.format) {
      case 'json':
        res.send(documents.map(function(d) {
          return { title: d.title, id: d._id };
        }));
      break;

      // Here
      default:
        res.send('Format not available', 400);
      break;
    }
  });
});

TJ Holowaychuk pointed this out to me in the comments.

I’m not sure if a 400 Bad Request is the best way to respond though. What about 406 Not Acceptable? I’m sure there’s a standard convention, but I read through part of rfc2616 and couldn’t decide.

An additional way to safeguard against hung connections is by using middleware to disconnect them after a period of time. The Connect-timeout middleware by Guillermo Rauch could be used to do this. Long-running connections are still allowed with this middleware by using the clearTimeout method which gets added to the request objects.

It’s fairly easy to use:

var connectTimeout = require('connect-timeout@0.0.1'),
    // ...

app.configure(function() {
  app.use(connectTimeout({ time: 10000 }));
  // ...

There are options for error code and time.

This week’s code is commit 2fde220.


blog comments powered by Disqus