Routing - gregerolsson/booster GitHub Wiki

The built-in Backbone.Router type fits in nicely in with the other Backbone types in that you declare routes (often at the top) that map to the various methods in your particular router. Views work the same; you declare events at the top that map to functions within the view.

Although symmetrical and fine, if you compare this to how frameworks like Sinatra and Express.js do it you loose the possibility of introducing middleware when setting up routes. In Express.js you can do the following

  var app = express.createServer();

  function loadExample(req, next) {
    req.example = /**
      load sample with ID req.params.example from DB asynchronously and
      invoke next() when fetched.
    **/;
  }

  app.get('/examples/:example', loadExample, function(req, res) {
    res.json(req.example)
  });

Here, Express.js will invoke the local loadExample function before the actual router function is invoked. Furthermore, loadExample may be asynchronous and forward the request to the router function at a later time once the information from the database has arrived, passing on any information down the middleware chain via the request object.

Things like these are not possible with the built-in Backbone.Router and router functions need to explicitly call helpers like loadExample and supply callbacks to handle the result.

In Booster we've added a thin layer on top of Backbone.Router that mimics the way Express.js solves routing.

A Single Router

First off, the Backbone support that Booster introduces for routing uses a single Backbone.Router instance for handling all of the application routes. From a technical standpoint using multiple router instances, which is the norm, does not impose any kind of scoping in the actual routing at run-time -- all the Backbone.Router instances in an application register their routes in the same Backbone.history singleton anyway.

However, routers also trigger events based on the name of the function in the router that is invoked. If you are used to writing code that often binds to router events you will need to be a bit careful when naming the router functions since there is only one router instance.

Note: In our own applications we have never needed to bind to a router:* event since the router functions in our case represent the top level functionality of the application. There is nothing "above them" that "controls the controllers/routers". That does not say that other applications do so, and if you depend on this a lot you may be better off using the more traditional way of dealing with routers.

That said, here is what it may look like in your application:

app/assets/javascript/routes/todos.js.booster

var app   = require('booster/router'),
    todo  = require('../models/todo'),
    views = require('../views/todo);

function loadTodo(req, next) {
  req.todo = new todo.Model({id: req.params.id});
  todo.fetch({ success: next });
}

app.route('/todos:id', loadTodo, function show(req) {
  var view = new views.Show({model: req.todo});
  /** Insert view in DOM somewhere **/
});

app.route('/todos:id/edit', loadTodo, function edit(req) {
  var view = new views.Edit({model: req.todo});
  /** Insert view in DOM somewhere **/
});

It is quite likely that you will have several files under routes/ that deal with a specific set of routes that are related to eachother. Since each file is a CommonJS-like module they can have their own set of private variables or helper functions like loadTodo.

Should you need to access the underlying Backbone.Router instance for some reason, it is exported along with the route function from the booster/router module:

app.router.bind('route:show', function() {
  console.log('The `show` route was invoked');
});

The name of the last middleware function in a call to route becomes the name of the route, in case you want to listen for events. In the example above, these are named show and edit, respectively. If you don't care about these events you can use anonymous functions.

Again, note that all routes are stored in the same Backbone.Router instance behind the scenes and if you use named functions that you want to listen for events on, make sure they are unique for the whole application.