Routing
Introduction
Fast, predictable HTTP routing with sub-2 micro-second performance. Supports parameters, wildcards, and multiple HTTP methods with consistent speed from 10 to 500+ routes. No edge cases. No performance degradation. Just reliable routing that scales.
Benchmarking
Benchmarks are benchmarks, they are only here for your reference. All benchmarks have been run on a full application bootstrap, using an Apple M3 Pro @ 3.5GHz with Mitata for accurate microsecond-level measurements. For more information, see benchmarks.
| Benchmark | Bun 1.3.5 | Node 22.19 | Deno 2.6.3 |
|---|---|---|---|
| Static routes (100) | 1.38 µs | 3.14 µs | 2.69 µs |
| Dynamic routes (100) | 1.48 µs | 3.09 µs | 2.44 µs |
| Mixed routes (70/30) | 1.38 µs | 2.87 µs | 2.29 µs |
| Worst case (last of 100) | 1.49 µs | 3.10 µs | 2.40 µs |
| Large router (500 routes) | 1.44 µs | 2.96 µs | 2.30 µs |
Installation
To start using the router, simply install into an existing Raptor application via the CLI or, if you are using Deno, import it directly from JSR.
deno add jsr:@raptor/routerbunx jsr add @raptor/routernpx jsr add @raptor/routeryarn dlx jsr add @raptor/routerpnpm dlx jsr add @raptor/routerType inference
To get full type inference with @raptor/router, you'll need to add type declarations to your project. This is a one-time setup that enables type-safe validation throughout your entire codebase.
Why is this needed?
JSR (JavaScript Registry) doesn't allow packages to modify global types directly. Since the validator extends the native Request object with a params property, we need to declare this package in your project.
The good news: Once configured, you get full autocomplete and type safety everywhere you use context.request.params.
Setup
In the root of your project (or in your src/ directory), create a file named types.d.ts:
// types.d.ts
import type { Params } from "@raptor/router";
declare global {
interface Request {
params: Params;
}
}
export {};Usage
The built-in router operates similarly to standard Raptor middleware, enabling you to define routes using familiar parameter patterns such as /users/:id.
Adding routes to the router
Using configuration, you can easily add routes to your application as follows:
import router, { Route } from "@raptor/router";
import { Kernel, Context, HttpMethod } from "@raptor/kernel";
const app = new Kernel();
app.use(router({
routes: [
new Route({
name: "index",
method: HttpMethod.GET,
pathname: "/",
handler: () => "Hello, Dr Malcolm!"
})
]
}));
app.serve();If you prefer, you can also instantiate the router yourself and add routes with the methods:
import { Router, Route } from "@raptor/router";
import { Kernel, Context, HttpMethod } from "@raptor/kernel";
const app = new Kernel();
const router = new Router();
router.add([
new Route({
name: "index",
method: HttpMethod.GET,
pathname: "/",
handler: () => "Hello, Dr Malcolm!"
})
]);
app.use(router.handle);
app.serve();Route Parameters
Route parameter values are processed and available via the router's context object context.request.params if they are found in the route's pathname. Make sure to add the types.d.ts definitions from above.
import { Route } from "@raptor/router";
import { type Context, HttpMethod } from "@raptor/kernel";
/* ... */
const route = new Route({
name: "person.read",
method: HttpMethod.GET,
pathname: "/person/:name";
handler: (context: Context) => {
const { name } = context.request.params;
return `Hello ${name}`;
}
});Route Middleware
Middleware can be added to individual routes by using the optional middleware property.
import { Route } from "@raptor/router";
import { type Context, Forbidden, HttpMethod } from "@raptor/kernel";
/* ... */
const authMiddleware = (context: Context, next: CallableFunction) => {
const customHeader = context.request.headers.get("X-Header");
if (!customerHeader) {
throw new Forbidden();
}
return next();
};
const route = new Route({
name: "user.index",
method: HttpMethod.GET,
pathname: "/users";
middleware: authMiddleware,
handler: () => "Hello, Dr Malcom!"
);
/* ... */The following code will check for the custom header's existence before continuing to the handler.
Multiple middleware
Multiple middleware functions can be added as an array. The system will run through each before finally moving on to the defined handler function. For more information, see Middleware.
Route Groups
Route Groups are a convenient and easy way to share functionality across multiple routes. You can configure prefixes and add middleware to multiple routes. You can add groups in exactly the same way you add routes to the router, with the add() method.
Prefixes
If you need to share a common prefix across multiple routes, you can add a name and path prefix to a Route Group.
// Setup a new group for our API routes.
const group = new RouteGroup({
name: "api.",
prefix: "/api"
});
// Define our routes for the API.
const routes = [
new Route({
name: "user.index",
pathname: "/users",
method: HttpMethod.GET,
handler: () => "OK"
})
];
// Add our routes to the group.
group.add(routes);
// Add the group to the router.
router.add(group);Middleware
If you want to share common middleware across multiple routes, you can add one or more to a group in exactly the same way as you can with a normal route. The middleware property accepts a Middleware instance or an array.
// Add a middleware to all of our API routes.
const group = new RouteGroup({
name: "api.",
prefix: "/api",
middleware: (context: Context, next: CallableFunction) => {
console.log("Running the route group middleware.");
return next();
}
});