tiny-request-router
Fast, generic and type safe router (match request method and path).
Features
- Minimal and opinionless router, can be used in any script and environment.
- Matches a request method (e.g.
GET
) and a path (e.g./foobar
) against a list of routes - Uses path-to-regexp, which is used by express and therefore familiar
- Allows wildcards (e.g.
/user/(.*)/age
) and named parameters (e.g./info/:username/:age
) - Will not call your handlers automatically, as it only cares about matching
- Battle hardened in production (Cloudflare Worker with 10M requests per day)
- No magic, no assumptions, no fluff, type safe, tested
Route testing
- You can use the Express Route Tester (select
2.0.0
) to debug your path patterns quickly
Installation
yarn add tiny-request-router
# or
npm install --save tiny-request-router
Usage (JavaScript/TypeScript)
import { Router } from 'tiny-request-router'
// NodeJS: const { Router } = require('tiny-request-router')
const router = new Router()
router
.get('/(v1|v2)/:name/:age', 'foo1')
.get('/info/(.*)/export', 'foo2')
.post('/upload/user', 'foo3')
const match1 = router.match('GET', '/v1/')
// => null
const match2 = router.match('GET', '/v1/bob/22')
// => { handler: 'foo1', params: { name: 'bob', age: '22' }, ... }
Make your handlers type safe (TypeScript)
import { Router, Method, Params } from 'tiny-request-router'
// Let the router know that handlers are async functions returning a Response
type Handler = (params: Params) => Promise<Response>
const router = new Router<Handler>()
router.all('*', async () => new Response('Hello'))
const match = router.match('GET' as Method, '/foobar')
if (match) {
// Call the async function of that match
const response = await match.handler()
console.log(response) // => Response('Hello')
}
Example: Cloudflare Workers (JavaScript)
Use something like wrangler to bundle the router with your worker code.
import { Router } from 'tiny-request-router'
const router = new Router()
router.get('/worker', async () => new Response('Hi from worker!'))
router.get('/hello/:name', async params => new Response(`Hello ${params.name}!`))
router.post('/test', async () => new Response('Post received!'))
// Main entry point in workers
addEventListener('fetch', event => {
const request = event.request
const { pathname } = new URL(request.url)
const match = router.match(request.method, pathname)
if (match) {
event.respondWith(match.handler(match.params))
}
})
API
Table of Contents
Method()
Type: ("GET"
| "POST"
| "PUT"
| "PATCH"
| "DELETE"
| "HEAD"
| "OPTIONS"
)
Valid HTTP methods for matching.
RouteOptions()
Extends: TokensToRegexpOptions
Optional route options.
Example:
// When `true` the regexp will be case sensitive. (default: `false`)
sensitive?: boolean;
// When `true` the regexp allows an optional trailing delimiter to match. (default: `false`)
strict?: boolean;
// When `true` the regexp will match to the end of the string. (default: `true`)
end?: boolean;
// When `true` the regexp will match from the beginning of the string. (default: `true`)
start?: boolean;
// Sets the final character for non-ending optimistic matches. (default: `/`)
delimiter?: string;
// List of characters that can also be "end" characters.
endsWith?: string;
// Encode path tokens for use in the `RegExp`.
encode?: (value: string) => string;
RouteMatch()
Extends: Route<HandlerType>
The object returned when a route matches.
The handler can then be used to execute the relevant function.
Example:
{
params: Params
matches?: RegExpExecArray
method: Method | MethodWildcard
path: string
regexp: RegExp
options: RouteOptions
keys: Keys
handler: HandlerType
}
Router
class:Tiny request router. Allows overloading of handler type to be fully type safe.
Example:
import { Router, Method, Params } from 'tiny-request-router'
// Let the router know that handlers are async functions returning a Response
type Handler = (params: Params) => Promise<Response>
const router = new Router<Handler>()
routes
.List of all registered routes.
all(path, handler, options)
.path
stringhandler
HandlerTypeoptions
RouteOptions (optional, default{}
)
Add a route that matches any method.
get(path, handler, options)
.path
stringhandler
HandlerTypeoptions
RouteOptions (optional, default{}
)
Add a route that matches the GET method.
post(path, handler, options)
.path
stringhandler
HandlerTypeoptions
RouteOptions (optional, default{}
)
Add a route that matches the POST method.
put(path, handler, options)
.path
stringhandler
HandlerTypeoptions
RouteOptions (optional, default{}
)
Add a route that matches the PUT method.
patch(path, handler, options)
.path
stringhandler
HandlerTypeoptions
RouteOptions (optional, default{}
)
Add a route that matches the PATCH method.
delete(path, handler, options)
.path
stringhandler
HandlerTypeoptions
RouteOptions (optional, default{}
)
Add a route that matches the DELETE method.
head(path, handler, options)
.path
stringhandler
HandlerTypeoptions
RouteOptions (optional, default{}
)
Add a route that matches the HEAD method.
options(path, handler, options)
.path
stringhandler
HandlerTypeoptions
RouteOptions (optional, default{}
)
Add a route that matches the OPTIONS method.
match(method, path)
.Returns: (RouteMatch<HandlerType> | null)
Match the provided method and path against the list of registered routes.
Example:
router.get('/foobar', async () => new Response('Hello'))
const match = router.match('GET', '/foobar')
if (match) {
// Call the async function of that match
const response = await match.handler()
console.log(response) // => Response('Hello')
}
More info
Please check out the tiny source code or tests for more info.
License
MIT