Coder Social home page Coder Social logo

http-hash's Introduction

http-hash

build status Coverage Status Davis Dependency status

NPM

HTTP router based on a strict path tree structure

Example 1 : Basic routes

var HttpHash = require('http-hash');

// Create a new http hash
var hash = HttpHash();

// Create a route mapping to /test/<anything but "/">
hash.set('/test/:foo/', function (req, res) {
    res.end();
});

// Get a valid route
var route = hash.get('/test/var');
console.log(route);
/*
-> {
    handler: function (req, res) {},
    params: {
        foo: 'var'
    },
    splat: null
}
*/

// Get an invalid route (returns null)
var missing = hash.get('/missing');
console.log(missing);
/*
-> {
    handler: null,
    params: {},
    splat: null
}
*/

Example 2 : Trailing splats

var HttpHash = require('http-hash');

// Create a new http hash
var hash = HttpHash();

// Create a route mapping to /foo/<anything but "/">/<anything>
hash.set('/foo/:test/*', function (req, res) {
    res.end();
});

var route = hash.get('/foo/val/one/two/three');
console.log(route);
/*
-> {
    handler: function (req, res) { ... },
    params: {
        test: 'val'
    },
    splat: 'one/two/three'
}
*/

Overview

The most popular node routers are based on regular expression matching. This means that the order in which the routes are defined affects the resolution of a route to handler. Sometimes this is desirable, but it would often be better to have a resolution scheme that is easier to reason about.

http-hash solves the routing problem by making route resolution independent of the order in which routes are defined. It does so by breaking a path into a tree of nodes, based on a simple split on /. For example, the route /foo/bar/baz is treated as tree nodes foo > bar > baz. We call foo, bar and baz path segments.

Theses path segments are arranged into a tree of nodes, where each segment defines a node in the tree. Each node can point to:

  • a fixed handler node.handler, otherwise known as the node value

  • a set of static paths indexed by path name node.staticPaths

  • a variable subtree node.variablePaths, that can match a single named parameter OR the remainder of a route (splat).

If the last character of a defined route is *, a variable path (or splat) will be inserted, consuming the rest of the path. This allows for subrouting, i.e. if you want to mount a static filesystem on /fs you would set the path as /fs/* where the nodes are broken down into the tree fs > *. The remainder of the route will be returned as a "splat" value, allowing for further routing.

In the simple case, the route tree is based on exact matches on the name of the segment. That is to say, for the case where we want to match /foo/bar/baz, the tree looks like

{
  staticPaths: {
    foo: {
      staticPaths: {
        bar: {
          staticPaths: {
            baz: {
              handler: function (req, res) {}
            }
          }
        }
      }
    }
  }
}

When defining routes, variable paths may be specified. This is where path segments are prefixed with : i.e. /foo/:bar/baz.

For the :bar segment, the route consumes the single variable route slot for that node in the tree. So for example, the route `/foo/:bar/baz looks like

{
  staticPaths: {
    foo: {
      variablePaths: {
        staticPaths: {
          'baz': function (req, res) {}
        }
      }
    }
  }
}

Since a node can have both static and dynamic paths associated with it, the static path will win over the variable path when we resolve the path.

Trailing slashes

In most cases the trailing / does not matter. Variables cannot be the empty string, and neither can splats. A splat value will not contain the leading slash as it is consumed by the parent node.

Path conflicts

If a path conflict occurs, an exception will be thrown. Conflicts occur when:

  • A route is defined twice, resolving to two handlers

This is the simplest case where /foo has been defined twice.

  • Variable names in the path are different

Note that /foo/:vara/ and /foo/:varb/ conflict, since they both resolve to foo.variablePaths, but have different param names.

  • A variable route is defined for a splat node

In the case of splats being defined at a level, no other other variables may be specified, as we cannot distinguish between /foo/:var and /foo/*. It is however ok to put static paths on the same level, i.e. /foo/bar and /foo/*. In this case, the static paths will be tried first before yielding the splat.

Docs

var hash = HttpHash()

http-hash := () => HttpHash

type HttpHash := {
    get: (String: url) => RouteResult,
    set: (String: path, Any: handler) => void,
    _hash: RouteNode
}

type RouteNode := {
    handler: Any,
    fixedPaths: Object<String, RouteNode>,
    variablePaths: RouteNode | null
}

type RouteResult := {
    handler: Any | null,
    params: Object<String, String>,
    splat: String | null
}

http-hash exports a safe constructor function that when called returns a new HttpHash. get and set methods are exposed for public consumption and the underlying data structure _hash is exposed for private inspection/internal use.

hash.set(path, handler)

hash.set := (String: path, Any: handler) => void

Puts a path in the route table. If the path conflicts with an existing path, an exception will be thrown.

Routes containing a * that are not part of a /* prefix will also throw an exception.

A path should look like / or /foo or /:foo or a union of theses things, or optionally end with /*

  • param names should not be repeated as they will conflict but there is no strong assertion for this. The last param name wins.

  • specifying a variable twice for a node will cause an exception

  • repeated and trailing '/' are ignored

  • paths are case sensitive

  • variables and splats are not matched by the empty string.

hash.get(path)

hash.get := (String: path) => RouteResult

Gets a route from the route table. If there is no viable route, the handler will be returned as null in the RouteResult object.

The route result contains a params hash, containing a key for each named variable in the path. Additionally, if a splat route was defined, the splat property will contain the tail portion of the route matched.

Installation

npm install http-hash

Tests

npm test

Contributors

  • Matt Esch

MIT Licensed

http-hash's People

Contributors

matt-esch avatar uberesch avatar kemitchell avatar michaelnisi avatar adispring avatar

Stargazers

 avatar Yannick Gauthier avatar sachin shinde avatar Rishikesh Darandale avatar M.K. avatar Cat  avatar Fred Daoud avatar  avatar Jimmy Huang avatar kimkr avatar Artem Sapegin avatar Yvan Wang avatar  avatar George Borisov avatar Michael Reinstein avatar Marcin Niezborała avatar Luis Del Águila avatar Ferdinand Salis avatar  avatar David Guttman avatar Andrés Felipe Chaparro avatar Lightning Koala avatar Ben Smith avatar Lan Qingyong avatar Jannis R avatar Daniel Madureira avatar Risto Novik avatar Nikita Dementev avatar Erik Vavro avatar nicohe avatar nireak avatar Joseph Dykstra avatar Nikos M. avatar Joseph avatar Tejesh Mehta avatar Nicola Del Gobbo avatar Matteo Collina avatar Kenan Sulayman avatar Yu-Shih Wang avatar Christopher Giffard avatar  avatar Jonathan Barnett avatar Bret Comnes avatar Matteo Capucci avatar Anthony Fiorani avatar Jacob Rask avatar xcatliu avatar chris avatar Gabe Montalvo avatar Jacky Alciné avatar juno suárez avatar Colin Meinke avatar Nick avatar Simon Escobar Benitez avatar Trevor Cowley avatar  avatar Guilherme Guerchmann avatar Tony Fabeen avatar João Cunha avatar Jeremy Cantrell avatar Shawn Allen avatar nichoth avatar Brandon Konkle avatar Don Abrams avatar Bent Cardan avatar Charlike Mike Reagent avatar Son avatar Peter Wong avatar Gil Birman avatar Roman Boiko avatar Alexander Gundermann avatar Gil avatar Thomas Peklak avatar Patrick Steele-Idem avatar Aleksey Smolenchuk avatar Jonathan Krause avatar

Watchers

James Cloos avatar Guilherme Guerchmann avatar Chris Brody avatar  avatar  avatar  avatar

http-hash's Issues

Clean example of routing depending on method (GET, POST)

I understand that this is outside of what this module is supposed to do but I'm not sure where else to ask this and this module should at least be involved.

How would you use this together with something else to run different functions depending on the HTTP method? For example if I want different handlers for GET /contacts and POST /contacts where one delivers all the contacts and the other adds a new contact?

should nesting hash routers work?

do we want this to work?

var Hash = require('http-hash')

var router = Hash()
var users = Hash()

users.set('/', function (req, res) {
  res.end('users root')
})

router.set('/users/*', users)

console.log(router.get('/users')) // gets 'users root' function in handler

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.