• Stars
    star
    321
  • Rank 130,752 (Top 3 %)
  • Language
    JavaScript
  • License
    Apache License 2.0
  • Created almost 15 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

liberal JSON-only HTTP request routing for node.

journey

liberal JSON-only HTTP request routing for node.

introduction

Journey's goal is to provide a fast and flexible RFC 2616 compliant request router for JSON consuming clients.

synopsis

var journey = require('journey');

//
// Create a Router
//
var router = new(journey.Router);

// Create the routing table
router.map(function () {
    this.root.bind(function (req, res) { res.send("Welcome") });
    this.get(/^trolls\/([0-9]+)$/).bind(function (req, res, id) {
        database('trolls').get(id, function (doc) {
            res.send(200, {}, doc);
        });
    });
    this.post('/trolls').bind(function (req, res, data) {
        sys.puts(data.type); // "Cave-Troll"
        res.send(200);
    });
});

require('http').createServer(function (request, response) {
    var body = "";

    request.addListener('data', function (chunk) { body += chunk });
    request.addListener('end', function () {
        //
        // Dispatch the request to the router
        //
        router.handle(request, body, function (result) {
            response.writeHead(result.status, result.headers);
            response.end(result.body);
        });
    });
}).listen(8080);

installation

$ npm install journey

API

You create a router with the journey.Router constructor:

var router = new(journey.Router);

You define some routes, with bound functions:

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:

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:

{ status: 200,
  headers: {"Content-Type":"application/json"},
  body: '{"journey":"Welcome"}'
}

Note that the response body will either be JSON data, or empty.

Routes

Here are a couple of example routes:

// route                                // matching request
router.get('/users')                    // GET    /users
router.post('/users')                   // POST   /users
router.del(/^users\/(\d+)$/)            // DELETE /users/45
router.put(/^users\/(\d+)$/)            // PUT    /users/45

router.route('/articles')               // *           /articles
router.route('POST',          '/users') // POST        /users
router.route(['POST', 'PUT'], '/users') // POST or PUT /users

router.root                             // GET /
router.any                              // Matches all request
router.post('/', {                      // Only match POST requests to /
    assert: function (req) {         // with data in the body.
        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:

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:

router.get('/hello').bind(function (req, res) {
    res.send(200, {}, {hello: "world"});
});

The send method is pretty flexible, here are a couple of examples:

                            // 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:

res.sendBody(JSON.stringify({hello:"world"}));

This will bypass JSON conversion.

URL parameters

Consider a request such as GET /users?limit=5, I can get the url params like this:

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.

Capture groups

Any captured data on a matched route gets passed as arguments to the bound function, so let's say we have a request like GET /trolls/42, and the following route:

get(/^([a-z]+)\/([0-9]+)$/)

Here's how we can access the captures:

router.get(/^([a-z]+)\/([0-9]+)$/).bind(function (req, res, resource, id, params) {
    res;      // response object
    resource; // "trolls"
    id;       // 42
    params;   // {}
});

Summary

A bound function has the following template:

function (request, responder, [capture1, capture2, ...], data/params)

Paths

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:

router.path('/domain', function () {
    this.get();        // match 'GET /domain'
    this.root;         // match 'GET /domain/'
    this.get('/info'); // match 'GET /domain/info'

    this.path('/users', function () {
        this.post();   // match 'POST /domain/users'
        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:

function authorize (request, body, cb) {
  return request.headers.authorized === true
      ? cb(null)
      : cb(new journey.NotAuthorized('Not Authorized'));
}

function authorizeAdmin (request, body, cb) {
  return request.headers.admin === true
      ? cb(null)
      : cb(new journey.NotAuthorized('Not Admin'));
}

Journey exposes this in three separate location through the filter API:

Set a global filter

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

var router = new(journey.Router)({ filter: authorize });

router.map(function () {
    this.filter(function () {
        //
        // Routes in this scope will use the 'authorize' function
        //
    });

    this.filter(authorizeAdmin, function () {
        //
        // Routes in this scope will use the 'authorizeAdmin' function
        //
    });
});

Set a filter on an individual route

var router = new(journey.Router)({ filter: authorize });

router.map(function () {
    this.get('/authorized').filter().bind(function (req, res, params) {
        //
        // This route will be filtered using the 'authorize' function
        //
    });

    this.get('/admin').filter(authorizeAdmin).bind(function (req, res, params) {
        //
        // This route will be filtered using the 'authorizeAdmin' function
        //
    });
});

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:

router.route('/articles').bind(function (req, res) {
    this.request.method; // "POST"
    res.send("Thanks for your " + this.request.method + " request.");
});

license

Released under the Apache License 2.0

See LICENSE file.

Copyright (c) 2010 Alexis Sellier

More Repositories

1

rx

๐Ÿ‘พ Modern and minimalist pixel editor
Rust
2,920
star
2

node-static

rfc 2616 compliant HTTP static-file server module, with built-in caching.
JavaScript
2,171
star
3

toto

the 10 second blog-engine for hackers
Ruby
1,473
star
4

http-console

simple, intuitive HTTP REPL โ€” Speak HTTP like a local.
JavaScript
1,358
star
5

eyes.js

a customizable value inspector for node.js
JavaScript
342
star
6

nakamoto

Privacy-preserving Bitcoin light-client implementation in Rust
Rust
303
star
7

thingler

The amazingly simple-to-use, real-time, collaborative todo list!
JavaScript
296
star
8

dorothy

a basic template for toto, the blogging engine
Ruby
244
star
9

hijs

simple & fast javascript syntax highlighting for the browser
JavaScript
240
star
10

rgx.legacy

Modern mid-level 2D graphics library
Rust
194
star
11

popol

Minimal non-blocking I/O for Rust
Rust
157
star
12

neovim-fuzzy

Minimalistic fuzzy file finding for neovim
Vim Script
111
star
13

dotfiles

~cloudhead
Vim Script
100
star
14

mutter

the tiny command-line interface library with lots of style~
Ruby
80
star
15

resourcer

a resource-oriented object-relational mapper for document databases
JavaScript
58
star
16

pixelog

simple pixel tracking server
Go
56
star
17

node-syslog

a syslog-ng TCP client, with basic fault-tolerance.
JavaScript
32
star
18

koi

minimal task management for hackers
Ruby
31
star
19

nonempty

Correct by construction non-empty list
Rust
30
star
20

px

Superseded by `rx`
C
30
star
21

erlapp.template

minimal erlang/OTP rebar template
Erlang
29
star
22

styleguide

style-guide for various languages
27
star
23

shady.vim

Shady vim color-scheme for late night hacking
Vim Script
23
star
24

vargs

practical variable argument handling in node.js
JavaScript
22
star
25

neovim-ghcid

Provides instant haskell error feedback inside of neovim, via ghcid.
Vim Script
21
star
26

microserde

miniserde minus the dependencies
Rust
18
star
27

pilgrim

a JSON consuming JavaScript XHR client for the browser
JavaScript
15
star
28

nimbus

nimble, durable, document store
JavaScript
14
star
29

spell-correct

ruby implementation of Norvig's spell corrector
Ruby
12
star
30

node-crawler

http crawler โ€” (project under development)
JavaScript
12
star
31

arbre

a dynamic functional programming language experiment
C
10
star
32

rig.js

temporally extend an object's capabilities.
JavaScript
9
star
33

mrchat

micro chat client for the console, in c, with some tasteful ncurses
C
9
star
34

talker-plugins

A bunch of plugins for Talker (http://talkerapp.com)
JavaScript
9
star
35

node-provider

Manage HTTP content providers. It's 'Accept'-based routing, folks!
JavaScript
9
star
36

proto.js

prototype & other core extensions
JavaScript
8
star
37

github-unwatcher

Unwatch github repos
JavaScript
8
star
38

memoir

Self-describing, reflective parser combinators
Rust
7
star
39

spinsv

a stateless service runner inspired by runit
Haskell
7
star
40

avl-auth

An authenticated AVL+ tree implementation in Haskell
Haskell
6
star
41

bloomy

Bloom filter implementation in Rust
Rust
6
star
42

gogol

a bitmap drawing & animation library for Go
Go
6
star
43

cloudhead.io

This is my website.
CSS
5
star
44

prose

lightweight text to html parser, combining the best of Markdown with a hint of Textile
Ruby
5
star
45

s3web

scripts for publishing static sites to S3
Ruby
4
star
46

github-recommend

recommendation engine for github's 2009 contest
C
4
star
47

monsv

a service runner inspired by runsv
Go
3
star
48

diffraction

even stocks can be diffracted!
Ruby
3
star
49

melon

hybrid javascript/ruby micro app engine
Ruby
3
star
50

node-intro

intro to node.js presentation for montreal.js
JavaScript
3
star
51

cryptocurrency

Cryptocurrency library for Haskell
Haskell
2
star
52

golem

pre-forking HTTP server for node (old)
JavaScript
2
star
53

ghoul

your (node) deployment minion - a work in progress.
PHP
2
star
54

rx.cloudhead.io

CSS
2
star
55

birth

graceful linux system birthing
Vim Script
2
star
56

extender

handy extensions to keep your code cleanโ„ข
Ruby
2
star
57

sokol-gfx-rs

C
1
star
58

iohk-challenge

IOHK Haskell test
Haskell
1
star
59

a-file-uploader

JavaScript
1
star
60

rgx

2D Graphics Toolkit for Rust
Rust
1
star