Express route middleware chains

In a web server each route handler usually has a single success and multiple failure cases. If we ignore all the failure cases we can reduce the handler to just a single line of code. Here is an example:

app.get '/home', (req, res) ->
    res.render 'home'

But most handlers are more complex than that, they need to fetch data from a database, validate input etc. These things can fail and we need to handle that. You could add this logic to the route handler, but especially with the asynchronous nature of nodejs, it would soon make your handlers unreadable. Here we fetch just two models from the database and already have four levels deep nesting:

app.get '/book/:book', (req, res) ->
  Book.fetch req.param('book'), (err, book) ->
    if err
      res.render '404'
    else
      Author.fetch book.author, (err, author) ->
        if err
           res.render '500'
        else
           res.render 'book', { book, author }

Thankfully experessjs gives us route middleware which we can use to keep the route handler a single line of code no matter how complex the logic is. Let's see how our code looks if we mode all the logic into route middleware:

fetchBook = (req, res, next) ->
  Book.fetch req.param('book'), (err, book) ->
    if err
      res.render '404'
    else
      req.book = book; next()

fetchBookAuthor = (req, res, next) ->
  Author.fetch req.book.author, (err, author) ->
    if err
      res.render '500'
    else
      req.author = author; next()

app.get '/book/:book, fetchBook, fetchBookAuthor, (req, res) ->
  res.render 'book', { book: req.book, author: req.author }

Even just looking at the route definition is pure joy: when a request comes in to /book/something, first call fetchBook, then fetchBookAuthor, and finally render the book template. Errors are handled in the middleware functions as soon as they occur, so we don't have to worry about them.

This is a rather simple example with just two middleware functions. But what if you have to check permissions, validate the input etc? I can easily imagine having dozens of middleware functions. This way you can structure your logic so that the code is never more than one or two levels of nesting deep, and that helps to keep the code comprehencible.