Catch-all routes in Backbone

Sooner or later, every Backbone application will encounter a route gone wrong. Handling mistyped URLs isn’t an absolute necessity, but it’s a polite gesture to provide an easy path back to known waters.

An approach to handling routing errors that will be very familiar to server-side MVC developers is the use of a “catch-all” route to match absolutely anything that falls through the cracks between the application’s defined routes. Adding the catch-all immediately before calling Backbone.history.start (more on that in a moment) looks like this:

Backbone.history.handlers.push({
  route: /(.*)/,
  callback: function (fragment) {
    // problems with `fragment` handled here
  }
});

// history can start now.
Backbone.history.start({ pushState: true });

No surprises. The “match-everything” route will pass the entire route fragment it receives on to the handler function for processing. The handler’s implementation will vary from one application to the next, but it will generally provide support for an empty (index) route as well as any non-existent URLs:

callback: function (fragment) {
  if (fragment == '') {
    // handle index route
  } else {
    alert("Sorry, '" + fragment + "' is unavailable");
  }
}

It may also be useful to separate fragment into its constituent parts. In that case, just split and test as with any other URL:

callback: function (fragment) {
  if (fragment == '') {
    // handle index route
  } else {
    fragment = fragment.split('/');
    if (fragment[0] == 'widgets') {
      // handle failed request for a widget
    } else {
      // handle all other failed requests
    }
  }
}

Two gotchas in the use of catch-all routes deserve at least a brief footnote.

First, the catch-all needs to be the very last route defined before Backbone.history.start() is called. Any earlier, and any routes intended to be processed by other routers will be caught up in the proceedings.

Second, if pushState support is enabled (i.e., by passing pushState: true to Backbone.history.start), any path set through the history API will fall through to the catch-all route. In most cases this won’t be an issue. If an application component is expecting to handle requests for a specific location outside of Backbone, however, the catch-all handler will need to be configured to ignore the route in question.

Let’s keep in touch

Get noise-free updates on software, product, and process.

Hey, I'm RJ: digital entomologist and intermittent micropoet, writing from the beautiful Rose City.