diff --git a/README.md b/README.md index 3fa28ba..d5d705f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ for *JSON* consuming clients. synopsis -------- +```javascript var journey = require('journey'); // @@ -47,38 +48,49 @@ synopsis }); }); }).listen(8080); +``` installation ------------ +```shell $ npm install journey +``` API --- You create a router with the `journey.Router` constructor: +```javascript var router = new(journey.Router); +``` You define some routes, with bound functions: +```javascript router.get('/hello').bind(function (req, res) { res.send('Hi there!') }); router.put('/candles').bind(function (req, res) { ... }); +``` *Note that you may also use the `map` function to define routes.* The `router` object exposes a `handle` method, which takes three arguments: an `http.ServerRequest` instance, a body, and a callback, as such: +```javascript function route(request, body, callback) +``` and asynchronously calls the callback with an object containing the response headers, status and body, on the first matching route: +```javascript { status: 200, headers: {"Content-Type":"application/json"}, body: '{"journey":"Welcome"}' } +``` Note that the response body will either be JSON data, or empty. @@ -86,6 +98,7 @@ Note that the response body will either be JSON data, or empty. Here are a couple of example routes: +```javascript // route // matching request router.get('/users') // GET /users router.post('/users') // POST /users @@ -103,33 +116,42 @@ Here are a couple of example routes: return req.body.length > 0; } }); +``` Any of these routes can be bound to a function or object which responds to the `apply` method. We use `bind` for that: +```javascript router.get('/hello').bind(function (req, res) {}); +``` If there is a match, the bound function is called, and passed the `response` object, as first argument. Calling the `send` method on this object will trigger the callback, passing the response to it: +```javascript router.get('/hello').bind(function (req, res) { res.send(200, {}, {hello: "world"}); }); +``` The send method is pretty flexible, here are a couple of examples: +```javascript // status, headers, body res.send(404); // 404 {} '' res.send("Welcome"); // 200 {} '{"journey":"Welcome"}' res.send({hello:"world"}); // 200 {} '{"hello":"world"}' res.send(200, {"Server":"HAL/1.0"}, ["bob"]); +``` As you can see, the body is automatically converted to JSON, and if a string is passed, it acts as a message from `journey`. To send a raw string back, you can use the `sendBody` method: +```javascript res.sendBody(JSON.stringify({hello:"world"})); +``` This will bypass JSON conversion. @@ -137,9 +159,11 @@ This will bypass JSON conversion. Consider a request such as `GET /users?limit=5`, I can get the url params like this: +```javascript router.get('/users').bind(function (req, res, params) { params.limit; // 5 }); +``` How about a `POST` request, with form data, or JSON? Same thing, journey will parse the data, and pass it as the last argument to the bound function. @@ -153,12 +177,14 @@ say we have a request like `GET /trolls/42`, and the following route: Here's how we can access the captures: +```javascript router.get(/^([a-z]+)\/([0-9]+)$/).bind(function (req, res, resource, id, params) { res; // response object resource; // "trolls" id; // 42 params; // {} }); +``` ### Summary # @@ -171,6 +197,7 @@ A bound function has the following template: Sometimes it's useful to have a bunch of routes under a single namespace, that's what the `path` function does. Consider the following path and unbound routes: +```javascript router.path('/domain', function () { this.get(); // match 'GET /domain' this.root; // match 'GET /domain/' @@ -181,11 +208,13 @@ Consider the following path and unbound routes: this.get(); // match 'GET /domain/users' }); }) +``` ### Filters # Often it's convenient to disallow certain requests based on predefined criteria. A great example of this is Authorization: +```javascript function authorize (request, body, cb) { return request.headers.authorized === true ? cb(null) @@ -197,17 +226,21 @@ Often it's convenient to disallow certain requests based on predefined criteria. ? cb(null) : cb(new journey.NotAuthorized('Not Admin')); } +``` Journey exposes this in three separate location through the `filter` API: #### Set a global filter +```javascript var router = new(journey.Router)({ filter: authorize }); +``` *Note: This filter will not actually be enforced until you use the APIs exposed in (2) and (3)* #### Set a scoped filter in your route function +```javascript var router = new(journey.Router)({ filter: authorize }); router.map(function () { @@ -223,9 +256,11 @@ Journey exposes this in three separate location through the `filter` API: // }); }); +``` #### Set a filter on an individual route +```javascript var router = new(journey.Router)({ filter: authorize }); router.map(function () { @@ -241,16 +276,19 @@ Journey exposes this in three separate location through the `filter` API: // }); }); +``` ### Accessing the request object # From a bound function, you can access the request object with `this.request`, consider a request such as `POST /articles`, and a route: +```javascript router.route('/articles').bind(function (req, res) { this.request.method; // "POST" res.send("Thanks for your " + this.request.method + " request."); }); +``` license -------