Home

Awesome

अनुमार्गक (anumargak)

Fastest HTTP Router

<div align="center"><img src="static/anumargak.png" width="300px"></div>

Known Vulnerabilities Travis ci Build Status Coverage Status NPM total downloads

<a href="https://www.patreon.com/bePatron?u=9531404" data-patreon-widget-type="become-patron-button"><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Become a Patron!" width="200" /></a> <a href="http://paypal.me/amitkumarguptagwl"> <img src="https://funcards.github.io/match-it/static/img/support_paypal.svg" alt="Paypal donate button" width="200"/></a>

Features

Usage

const http = require('http')
const router = require('anumargak')({
  defaultRoute : defaultHandler,//it'll be called when no route matches. If it is not set the we'll set statusCode to 404
  ignoreTrailingSlash: true,
  ignoreLeadingSlash: true,
  allowUnsafeRegex: false
})

router.on('GET', '/', (req, res, params) => {
  //process the request response here
})

const server = http.createServer((req, res) => {
  router.lookup(req, res)
})

server.listen(3000);

on(method, url [, options] , handler [, store] )

To register a route.

router.on("GET", "/this/is/static", handler);
router.on(["POST","PUT"], "/this/is/static", handler);

Dynamic URL

You can register dynamic url with multiple path paramters

router.on("GET", "/this/is/:dynamic", handler);
router.on("GET", "/this/is/:dynamic", handler);//it will error
router.on("GET", "/this/is/:dynamic/with/:pattern(\\d+)", handler);
//Eg: params = { dynamic : val, pattern: 123}
router.on("GET", "/this/is/:dynamic/with/:two:params", handler);// multiple parameters
router.on("GET", "/this/is/:dynamic/with/:two(\\d+):params", handler);//multiple parameters with pattern
router.on("GET", "/this/is/:dynamic/with/:two(\\d+)rest", handler);//single parameter

Enumerated URL

Anumargak handls enumerated URLs in static way. Because static URLs can be looked up faster than dynamic URLs.

router.on("GET", "/login/as/:role(admin|user|staff)", handler);

wildcard

wild cards are helpful when a route handler wants to control all the underlying paths. Eg. a handler registered with /help* may take care of all the help pages and static resources under the same path. You can check आलेख (Aalekh) for live example.

//this/is/juglee/and/
//this/is/juglee/and/wild
//this/is/juglee/and/wild/and/unknown
router.on("GET", "/this/is/:dynamic/and/*", handler);

//this/is/juglee/and/wild
//this/is/juglee/and/wildlife
//this/is/juglee/and/wild/and/unknown
router.on("GET", "/this/is/:dynamic/and/wild*", handler);

shorthand methods

var router = Anumargak();

router.get("/this/is/:dynamic", () => 30);
router.head("/this/is/:dynamic", () => 30);
router.post("/this/is/:dynamic", () => 30);
router.put("/this/is/:dynamic", () => 30);
router.delete("/this/is/:dynamic", () => 30);

off(method, url [, version] )

To remove a registered route. If no route is found no error will be thrown.

anumargak.off("GET", "/this/is/static");
anumargak.off("GET", "/this/is/:dynamic");
anumargak.off("GET", "/this/is/*/really/wild");
anumargak.off("GET", "/login/as/:role(admin|user|staff)"); //it'll delete all the versions

Enumerated URLs

an enumerated URL can be deleted multi steps

anumargak.off("GET", "/login/as/:role(admin|user|staff)");
//or
anumargak.off("GET", "/login/as/admin");
anumargak.off("GET", "/login/as/user");
anumargak.off("GET", "/login/as/staff");
//or
anumargak.off("GET", "/login/as/:role(user|staff)");
anumargak.off("GET", "/login/as/admin");

Versioned URLs

version can be provided as an additional parmeter. Valid values are:

anumargak.off("GET", "/this/is/static", version);

Please note that, if you delete a route without specifying versions then all the versioned routes will also be deleted.

find(method, url [, version])

To find a registered route. It returns;

{
  handler : function(){}, //registered function
  params : {}, //path parameters
  store : any // extra data provided at the time of registering the route
}

quickFind(method, url [, version])

To find a registered route. It returns;

{
  handler : function(){}, //registered function
  store : any // extra data provided at the time of registering the route
}

lookup(request, response, store)

This method reads request object to fetch url, method, and accept-version header to find matching route and then run the handler.

The handler should accept: request, response, and store. request._path.params is an object of path parameters.

Lookup method also save _path, _queryStr, and _hashStr in request object to save re-effort of spliting them. _path is an object with two properties: url, params.

lookupWithEvents(request, response, store)

save as above but raises events

count

You can always check how many routes are registered. If you delete some routes count will be decreased.

Other detail

Similar but not same URLs

You can register the URLs which look similar but not exactly same.

const anumargak = require('anumargak')()
//this/is/my/75
anumargak.on("GET", "/this/is/my/:age([0-9]{2,3})", handler);

//this/is/my/amit
anumargak.on("GET", "/this/is/my/:name([a-zA-z]+)", handler);

anumargak.on("GET", "/login/as/:role(admin|user|staff)", handler);
anumargak.on("GET", "/login/as/:role(developer|tester|hacker)", handler);

Named Expressions

Anumargak lets you add named expressions. You can use them at the time of registering the route.


router.addNamedExpression("num","\\d+");
router.addNamedExpression({
   "name1" : "regx1",
   "name2" : "regx2",
});

Example routes

/send/to/:phone(:phone:)
/authenticate/:token(:alphanum:)

Adding them make this router simple to use.

accept-version

Same routes can be registerd with different versions. Lookup method reads accept-version header to read the version or you can pass the version in find method directly.

router.on('GET', '/some/route', { version: '1.2.0' }, (req, res, params) => {
  //..
})

router.get( '/some/route', { version: '1.2.3' }, (req, res, params) => {
  //..
})

router.find('GET', '/some/route', "1.2.3");
router.lookup(req, res);

Note that

Events

You can register events.

router.on(eventName, fn);

Following events are supported;

These events will be called when you use lookupWithEvents().

Mappings

Anumargak supports

router.on( "single/url", fn );
router.on( "single/url", [fn, fn] );
router.on( ["single/url", "other/url" ], fn );
router.on( ["single/url", "other/url" ], [fn, fn] );

Benchmark

MethodURL typeअनुमार्गक (Anumargak) v1.7.0Find My Way v1.15.1
Findstatic24369856.072614866.631
Finddynamic2405576.1221106656.051
Finddynamic + query param1665114.8061082533.894
FindEnum23151436.161298019.289
Findwildchar2591342.6381630995.248
Findversioned static1958139.854263611.4248
Findversioned Dynamic465584.9696315857.0792
Lookstatic23614465.422201314.59
Lookdynamic2032592.029940862.1238
Lookdynamic + query param1403264.652923533.114
LookEnum12662923.031066446.935
Lookwildchar2251611.4491335382.238
Lookversioned static1313886.0550
Lookversioned Dynamic392754.437252312.1124

Note : Above benchmark has been taken on 16gb RAM ubuntu 17.10 machine with node v9.5.0 and npm v5.6.0

chart

Integration with other web frameworks

Muneem framework is already based on Anumargak. To use it with express js;

const app = require("express")();
const Anumargak = require("anumargak");
const router = new Anumargak();

app.use((req,res) => router.lookup(req, res));

router.on("GET", "/", (req, res) => {
    //..
});

app.listen(3002);

Use it with restana

const anumargak = require('anumargak')
const service = require('restana')({
  routerFactory: (options) => {
    return anumargak(options)
  }
})

service.get("/this/is/static", function(req, res){
    res.send("Hello");
})

service.get("/this/is/:dynamic", function(req, res){
    res.send("Hello");
})


service.start(3001).then((server) => {
    console.log("server has been started on port 3001")
});

Worth to mention

Contributors

Disclaimer

I initially used find-my-way npm package for मुनीम (Muneem) framework. But then I realized that lookup for static URLs is comparitively slower. Hence I develop this library. If you notice, I tried to keep the naming convention and syntaxes common wherever possible to reduce the time to switch from one library to another and to keep learning curve smaller.