-
-
Save jrson83/53eeaef8b3a07701dbf2396596479958 to your computer and use it in GitHub Desktop.
basic router in typescript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type Route = { | |
method: "*" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "CONNECT" | "TRACE" | |
path: string | |
regexp: RegExp | |
handler: (request: Request, route: MatchedRoute) => Promise<Response> | |
} | |
export type MatchedRoute = Route & { | |
url: URL | |
} | |
// excellent regex from kwhitely/itty-router | |
// it takes a path and turns it into a regex for later matching | |
function pathToRegexp(path: string): RegExp { | |
return RegExp(`^${path | |
.replace(/(\/?)\*/g, '($1.*)?') // trailing wildcard | |
.replace(/(\/$)|((?<=\/)\/)/, '') // remove trailing slash or double slash from joins | |
.replace(/(:(\w+)\+)/, '(?<$2>.*)') // greedy params | |
.replace(/:(\w+)(\?)?(\.)?/g, '$2(?<$1>[^/]+)$2$3') // named params | |
.replace(/\.(?=[\w(])/, '\\.') // dot in path | |
.replace(/\)\.\?\(([^\[]+)\[\^/g, '?)\\.?($1(?<=\\.)[^\\.') // optional image format | |
}/*$`) | |
} | |
export class Router { | |
routes: Route[] = [] | |
add(method: Route["method"], path: Route["path"], handler: Route["handler"]) { | |
this.routes.push({ | |
method, | |
path, | |
handler, | |
regexp: pathToRegexp(path) | |
}) | |
} | |
match(request: Request, url: URL): Route | undefined { | |
return this.routes | |
.filter(route => route.method === request.method || route.method === "*") | |
.find(route => route.regexp.test(url.pathname)) | |
} | |
async handle(request: Request): Promise<Response> { | |
let response, match, url = new URL(request.url) | |
const route = this.match(request, url) | |
if (route) { | |
return route.handler(request, {...route, url}) | |
} else { | |
return new Response(null, { status: 404 }) | |
} | |
} | |
} | |
// Use it like... | |
const router = new Router() | |
router.add("GET", "/users/:username", async (request, route) => { | |
console.log(route.regexp, route.url.pathname) | |
const username = route.regexp.exec(route.url.pathname)?.groups?.username | |
return new Response(`Matched ${route.path}, Hello ${username}!`) | |
}) | |
router.add("POST", "/users", async (request, route) => { | |
const fd = await request.formData() | |
return new Response(`Matched ${route.path}:\n${JSON.stringify(Array.from(fd.entries()))}`) | |
}) | |
router.add("*", "/foo", async (request, route) => { | |
return new Response("FOooooo!") | |
}) | |
// then somewhere where you have a request... | |
router.handle(request) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment