Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Node.js / Express.js Routing Re-imagined
JavaScript Makefile
branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
test
.gitignore
.travis.yml
LICENSE
Makefile
README.md
index.js
package.json
route.js
serve.js

README.md

detour

Build Status Coverage Status NPM version

Detour is a router for node.js web applications.

Detour is different from sinatra-style routers (like express's router) because you route urls to objects (that have http methods) instead of to http methods.

Rationale: If you have multiple http methods implemented for a given url (like a lot of APIs do), this style of routing will be much more natural and will vastly improve your code organization and re-use. With object routing, it's much simpler to keep the related handlers together, but separated from unrelated handlers (often even in another file/module).

It works for node.js' standard HTTP server, as well as express and connect applications.

Examples:

Using it in a plain node.js web app:

  var detour = require('detour');
  var http   = require('http');
  var router = detour();

  router.route('/', {
    GET : function(req, res){
      res.end("GET works!");
    },
    POST : function(req, res){
      res.end("POST works too!");
    }
  });

  http.createServer(function(req, res){
    router.middleware(req, res);
  }).listen(9999);

Using it in an express app:

  var detour = require('detour');
  var express = require('express');
  var app = express();
  var router = detour();
  app.use(router.middleware);

  router.route('/', {
    GET : function(req, res){
      res.end("GET works!");
    },
    POST : function(req, res){
      res.end("POST works too!");
    }
  });

Path Variables:

A simple example:

  router.route('/test/:test_id', {
    GET : function(req, res){
      res.end("GET works: " + req.pathVar.test_id);
      // a request to /test/1234 will have output like this:
      //   GET works: 1234

      // req.pathVar is a hash of all name value pairs pull from the
      // request url
    },
  });

In the example above, you can see that the path string sent to the router (/test/:test_id) uses the same format as express. Regular expressions are also allowed.

You can also see from the example that within a route handler, req.pathVar will have all the variables collected from your route.

connect-style middleware

Detour supports connect-style middleware like most node.js frameworks (including express). Example:

  var detour = require('detour');
  var router = detour();
  router.use(someMiddleware);

The middleware stack for detour runs after the route has been determined, but before any other processing. The entire routed resource object will be available at req.detourHandler if you want to do any special handling in your middleware that are related to its contents.

This makes Detour extremely extensible on a resource-by-resource basis. For example, if you write a resource object like this:

  router.route('/test/:test_id', {
    mustBeAuthenticated : true,   // <-- NB: non-standard property
    GET : function(req, res){
      res.end("GET works: " + req.pathVar.test_id);
    },
  });

...you can write a middleware that acts according to that data before the handler for GET is called.

collections

In addition to regular object routing, For the sake of convenience, Detour also supports collection-routing where two resource objects can be routed in a collection-member relationship at the same time.

Here's an example:

var router = new Router();
router.collection('/test/:testid', {
  collection : {
    GET : function(req, res){
      res.end('collection here!');
    }
  },
  member : {
    GET : function(req, res){
      res.end('member here! ' + JSON.stringify(req.pathVar));
    }
  }
});

Note: the object being routed has two properties: collection and member, with each being a resource object on its own. The member resource object will be routed to the given path (/test/:testid in this case), and the collection resource object will be routed to the parent path (/test in this case).

This feature can be useful if you have resources that follow this pattern and you want to keep these two similar resources together in the same file.

Method defaults and overrides:

OPTIONS handling:

There is a default OPTIONS response for any route but you can over-ride it like this:

router.on('OPTIONS', function(req, res, resource){
  // resource is the object you routed.  You can loop through the methods on it here if you want!
  res.writeHead(200);
  res.end("options, options, options!");
});

HEAD handling:

There is a default HEAD response for any route with a GET, but you can over-ride it like this:

router.on('HEAD', function(req, res, resource){
  // resource is the object you routed.  You can call resource.GET() from here if you want!
  res.writeHead(200);
  res.end("doesn't matter what I type here.  node will not send output on a HEAD request")
});

Error defaults and overrides:

405 handling:

The correct http response for a method that an existing url doesn't support is a 405. You can over-ride the default like this:

router.on(405, function(req, res, resource){
  // resource is the object you routed.  You can loop through the methods on it here if you want!
  res.writeHead(405);
  res.end("this resource doesn't support that method! ");
});

404 handling:

There is a default 404 response for any route that doesn't match, but you can over-ride it like this:

router.on(404, function(req, res){
  res.writeHead(404);
  res.end("nope. couldn't find that!")
});

(NOTE: this only works if the middleware doesn't get a third parameter -- typically called next() -- passed to it, otherwise next() is used for 404s as you'd expect from a middleware.)

Something went wrong with that request. Please try again.