instant-router
v1.0.1
Published
Ultra fast router using RadixTree data structure to match incoming HTTP requests.
Downloads
28
Maintainers
Readme
Instant Router
Ultra fast router to match incoming HTTP requests and generate urls.
- Uses a radix tree datastructure.
- Highly optimized to match static routes, check the benchmarks.
- UrlGenerator for relative/absolute urls.
- No middlewares.
- Framework independent.
Summary
Install
npm install instant-router
yarn add instant-router
Usage
Matching
import Router, { HttpContext, RouteDefinition, MethodNotAllowedError, RequestContext, ResourceNotFound } from 'instant-router';
import http, { IncomingMessage, ServerResponse } from 'node:http';
const routes: RouteDefinition[] = [
{
path: '/static',
controller: ({ res }: HttpContext) => res.end('Hello, World!')
},
{
path: '/users/:name',
requirements: { name: '[a-zA-Z]+' },
controller: ({ params, res }: HttpContext) => res.end(`Hello ${params.name}!`)
}
]
const router = new Router()
routes.forEach(route => router.addRoute(route))
http.createServer((request: IncomingMessage, res: ServerResponse) => {
try {
const req = RequestContext.fromIncomingMessage(request)
const { controller, params } = router.match(req);
controller({ req, res, params })
} catch (error) {
console.error(error)
if (error instanceof ResourceNotFound) {
res.writeHead(404)
res.end('404 - Resource not found')
} else if (error instanceof MethodNotAllowedError) {
res.writeHead(405)
res.end(`405 - Method ${request.method} not allowed`)
} else {
res.writeHead(500)
res.end('500 - Internal server error')
}
}
}).listen(3000, () => console.log('Listening on localhost:3000'))
:arrow_up::arrow_up::arrow_up: Go back to summary
Url generation
import Router from 'instant-router';
const router = new Router();
// Name the routes to use UrlGenerator
router.addNamedRoute('comment', {
path: '/posts/:postId/comments/:commentId',
methods: ['GET'], // default
controller: () => {}
})
const url = router.generateUrl('comment',
{
postId: 1,
commentId: 10,
foo: 1 // This is not a route parameter, so it will be added to the url as a query param.
},
{
isAbsolute: true,
// scheme: 'http',
// host: 'localhost',
// port: 3000
}
)
console.log(url); // "http://localhost:3000/posts/1/comments/10?foo=1"
:arrow_up::arrow_up::arrow_up: Go back to summary
Router class
Adding routes
To add a route, you must use the addRoute
method.
Example:
router.addRoute({
path: '/users/:id',
methods: ['GET'],
requirements: { name: '\\d+' },
controller: ({ res }: HttpContext) => res.end("Matched!")
})
path
- Should be a string that represents the URL path for the route.
- Can contain parameters in the format of
/my-path/:parameterName
. - Parameters in the URL path can be constrained by a regex using the
requirements
property.
methods
- Is an optional string or an array of strings that represents the HTTP methods. Default:
GET
- Valid methods are defined automatically via the route configuration or explicitly using the
addMethods
method.
requirements
- Is an optional object that defines validation requirements for parameters in the URL path.
- Keys (parameter name) and values (regex) must be of type string.
:warning: The router automatically adds the start and end delimiters, do not add them by yourself.
Bad: "^\\d+$"
, good: "\\d+"
controller
- Should be a function that represents the handler for the route.
- Take
HttpContext
({req, res, params}
) as an argument. Note that it will be your responsibility to call the controller.
:arrow_up::arrow_up::arrow_up: Go back to summary
Matching
To try to match a route from an HTTP request, you must use the match
method.
Example:
const context = new RequestContext('/users/1', 'GET')
const { controller, params } = router.match(context);
The RequestContext
class help you to pass relevant HTTP request data
to the match
method. You can read its documentation here.
The match
method returns a MatchedTreeNode
containing the properties controller
and params
.
Errors
- If the request HTTP method does not match any existing route, a
MethodNotAllowedError
is triggered. - If no route is matched, a
ResourceNotFound
error is triggered.
See Usage
for examples.
:arrow_up::arrow_up::arrow_up: Go back to summary
Generate urls
To generate urls, you can use the generateUrl
method.
Example:
// Name the routes to use UrlGenerator
router.addNamedRoute('comment', {
path: '/posts/:postId/comments/:commentId',
methods: ['GET'],
controller: () => {}
})
const url = router.generateUrl('comment',
{ postId: 1, commentId: 10 },
{ isAbsolute: true }
)
name
Is the name of the route to generate the URL for.
parameters
Optional object containing key-value pairs of route parameter names. If a passed parameter does not match any route parameter, it is added to the url as a query parameter.
options
type urlGeneratorOptions = {
isAbsolute?: boolean
scheme?: string
host?: string
port?: number
}
You can set the default options directly when you instantiate Router
:
const options = {
urlGenerator: {
isAbsolute: true,
scheme: 'https', // must be defined if isAbsolute is true
host: 'example.com', // must be defined if isAbsolute is true
port: 443 // optional even if isAbsolute is true
}
};
const router = new Router(options);
:arrow_up::arrow_up::arrow_up: Go back to summary
RequestContext class
The RequestContext
class help you to pass relevant HTTP request data to the match
method.
Usage:
import http from 'node:http';
import { RequestContext } from 'instant-router'
http.createServer((req, res) => {
const context = RequestContext.fromIncomingMessage(req)
console.log(context.path) // "/users/1"
console.log(context.method) // "POST"
})
By default, RequestContext
supports only the following http methods:
GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
But you can add more freely via the static property availableHttpMethods
:
RequestContext.availableHttpMethods.push("FOO")
const context = new RequestContext("/hello", "FOO")
console.log(context.method) // "FOO"
:arrow_up::arrow_up::arrow_up: Go back to summary
Benchmarks
Benchmark comparisons, adapted from: https://github.com/delvedor/router-benchmark/tree/master/benchmarks
Machine
linux x64 | 8 vCPUs | 7.6GB Mem
Software versions
- node: 18.14.2
=====================
instant-router benchmark
=====================
short static: 232,598,222 ops/sec
static with same radix: 56,142,144 ops/sec
dynamic route: 3,162,154 ops/sec
mixed static dynamic: 3,150,744 ops/sec
long static: 55,622,346 ops/sec
all together: 1,380,149 ops/sec
=======================
find-my-way benchmark
=======================
short static: 17,099,450 ops/sec
static with same radix: 6,243,196 ops/sec
dynamic route: 3,293,502 ops/sec
mixed static dynamic: 4,176,557 ops/sec
long static: 3,863,775 ops/sec
all together: 876,926 ops/sec
=======================================================
express router benchmark (WARNING: includes handling)
=======================================================
short static: 2,111,590 ops/sec
static with same radix: 1,832,877 ops/sec
dynamic route: 1,087,600 ops/sec
mixed static dynamic: 831,342 ops/sec
long static: 860,493 ops/sec
all together: 221,828 ops/sec
:arrow_up::arrow_up::arrow_up: Go back to summary