Awesome
अनुमार्गक (anumargak)
Fastest HTTP Router
<div align="center"><img src="static/anumargak.png" width="300px"></div><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
- Fastest node js router (as far as Google & I know)
- Framework independent.
- Supports static and dynamic both type of URLs.
- Supports path parameter with defined type
\this\is\:num([0-9]+)
- Support multiple path parameters.
- Handles enumerated URLs
\login\as\:role(admin|staff|user)
- Supports wildchar
\this\is\*
,\this\is\wild*
- You need not to register 2 separate routes for trailing slash.
\like\me\
and\like\me
. - You may skip leading slash while registering the route, though not recommended.
\like\me
andlike\me
. - Capture parameters' value for dynamic URLs.
- Add similar but not same URLs :
\this\is\:age([0-9]+)
and\this\is\:name([a-zA-Z]+)
- Support shorthand methods
- You can always have a count on registered routes.
- Supports versioned routes.
- You can name regular expressions to increase code redability.
- Support events
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:
- 1.2.3 : It'll delete only one URL
- 1.2.x : It'll delete all the URLs with different patche versions : 1.2.0, 1.2.1 ...
- 1.x : It'll delete all the URLs with different minor versions : 1.2.0, 1.2.1, 1.3.5 ...
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
}
- alternate signature
quickFind(request)
quickFind()
is faster thanfind()
as it doesn't process path parameters.
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
- A route can be registered with and without version. In this case, if the
accept-version
header presents then versioned route handler will be returned. - Value of
accept-version
header can be : "1.2.0", "1.2.x", "1.x", "*"
Events
You can register events.
router.on(eventName, fn);
Following events are supported;
- request - When lookup method is called.
- found - Before calling registered handler. (alias: route )
- not found - When there is no registered handler. (alias: default )
These events will be called when you use lookupWithEvents()
.
Mappings
Anumargak supports
- one-to-one mapping: One url to one handler
router.on( "single/url", fn );
- one-to-many mapping: One url to series of handlers
router.on( "single/url", [fn, fn] );
- many-to-one mapping: Multiple urls to one handler
router.on( ["single/url", "other/url" ], fn );
- many-to-one mapping: Multiple urls to series of handlers
router.on( ["single/url", "other/url" ], [fn, fn] );
Benchmark
Method | URL type | अनुमार्गक (Anumargak) v1.7.0 | Find My Way v1.15.1 |
---|---|---|---|
Find | static | 24369856.07 | 2614866.631 |
Find | dynamic | 2405576.122 | 1106656.051 |
Find | dynamic + query param | 1665114.806 | 1082533.894 |
Find | Enum | 23151436.16 | 1298019.289 |
Find | wildchar | 2591342.638 | 1630995.248 |
Find | versioned static | 1958139.854 | 263611.4248 |
Find | versioned Dynamic | 465584.9696 | 315857.0792 |
Look | static | 23614465.42 | 2201314.59 |
Look | dynamic | 2032592.029 | 940862.1238 |
Look | dynamic + query param | 1403264.652 | 923533.114 |
Look | Enum | 12662923.03 | 1066446.935 |
Look | wildchar | 2251611.449 | 1335382.238 |
Look | versioned static | 1313886.055 | 0 |
Look | versioned Dynamic | 392754.437 | 252312.1124 |
Note : Above benchmark has been taken on 16gb RAM ubuntu 17.10 machine with node v9.5.0 and npm v5.6.0
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
- BigBit standard : A standard to reprent any number in the universe in comparitively less space and without precision loss. A standard to save space to represent any text string in comparision of UTF encoding.
- imglab : Speedup and simplify image labeling / annotation process online. Supports multiple formats, one click annotation, easy interface and much more.
- stubmatic : A stub server to mock behaviour of HTTP(s) / REST / SOAP services. You can also mock binary formats.
- मुनीम (Muneem) : A webframework made for all team members.
- शब्दावली (shabdawali) : Amazing human like typing effects beyond your imagination. Check वार्ता for talking/chatting effect.
Contributors
- <img src="https://avatars2.githubusercontent.com/u/7692328?v=4" width="20" height="20"/> amitguptagwl
- <img src="https://avatars2.githubusercontent.com/u/10572008?v=4" width="20" height="20"/> rajeshdh
- <img src="https://avatars3.githubusercontent.com/u/4491530?v=4" width="20" height="20"/> shuklajay117
- <img src="https://avatars1.githubusercontent.com/u/37378042?v=4" width="20" height="20"/> Javi Garcia-Bengochea
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.