Skip to content

Instantly share code, notes, and snippets.

@hnordt
Created November 14, 2025 17:18
Show Gist options
  • Select an option

  • Save hnordt/2b96ecc2ecefe088f0b8208bf41ccf0e to your computer and use it in GitHub Desktop.

Select an option

Save hnordt/2b96ecc2ecefe088f0b8208bf41ccf0e to your computer and use it in GitHub Desktop.
type TMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
type THandler = (
req: Request,
match: URLPatternResult | null,
) => Response | Promise<Response>;
class Route {
constructor(
private pattern: URLPatternInput,
private handlers: Partial<Record<TMethod, THandler>>,
) {}
test(pattern: URLPatternInput) {
return new URLPattern(this.pattern).test(pattern);
}
exec(req: Request) {
const handler = this.handlers[req.method as TMethod];
if (!handler) {
throw new Error("Method not allowed");
}
return handler(
req,
new URLPattern(this.pattern).exec(req.url),
);
}
}
function ok(...args: Parameters<typeof String.raw>) {
return new Response(String.raw(...args), {
headers: { "Content-Type": "text/html; charset=utf-8" },
status: 200,
});
}
function notFound(...args: Parameters<typeof String.raw>) {
return new Response(String.raw(...args), {
headers: { "Content-Type": "text/html; charset=utf-8" },
status: 404,
});
}
const routes = [
new Route({ pathname: "/books", search: "{page=:page}?" }, {
async GET(req, match) {
return ok`<h1>Books - Page ${match?.search.groups.page ?? 1}</h1>`;
},
}),
new Route({ pathname: "/books/:id" }, {
async GET(req, match) {
return ok`<h1>Book ID ${match?.pathname.groups.id}</h1>`;
},
}),
];
Deno.serve((req: Request) => {
const url = new URL(req.url);
for (const route of routes) {
if (route.test(url)) {
return route.exec(req);
}
}
return notFound`<h1>Not found</h1>`;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment