Careeroza
Real World Example
A Type-safe Mini Router
Next.js typed routes, Hono and tRPC give you autocomplete and errors for URLs. This mini router types the handler params from the route pattern, like Express with typed paths.
Example
type ParamNames<P extends string> =
P extends `${string}:${infer Name}/${infer Rest}` ? Name | ParamNames<Rest>
: P extends `${string}:${infer Name}` ? Name : never;
type Handler<P extends string> = (params: { [K in ParamNames<P>]: string }) => string;
class Router {
private routes: { regex: RegExp; keys: string[]; handler: (p: Record<string, string>) => string }[] = [];
get<P extends string>(pattern: P, handler: Handler<P>): this {
const keys = [...pattern.matchAll(/:(\w+)/g)].map((m) => m[1]);
const regex = new RegExp("^" + pattern.replace(/:\w+/g, "([^/]+)") + "$");
this.routes.push({ regex, keys, handler: handler as (p: Record<string, string>) => string });
return this;
}
handle(url: string): string {
for (const r of this.routes) {
const m = url.match(r.regex);
if (m) return r.handler(Object.fromEntries(r.keys.map((k, i) => [k, m[i + 1]])));
}
return "404 Not Found";
}
}
const router = new Router()
.get("/products/:id", ({ id }) => `Product page for ${id}`)
.get("/users/:userId/orders/:orderId", ({ userId, orderId }) => `Order ${orderId} of ${userId}`);
["/products/42", "/users/u7/orders/OD-9", "/cart"].forEach((u) => console.log(u.padEnd(22), "->", router.handle(u)));Output
/products/42 -> Product page for 42 /users/u7/orders/OD-9 -> Order OD-9 of u7 /cart -> 404 Not Found