Last active
November 1, 2022 16:24
-
-
Save arisris/1c7eedc5079e4c98e74bbfafd59b2266 to your computer and use it in GitHub Desktop.
Simple Http Router Using JS Proxy Hack
This file contains 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 TRouter<Value, Method extends string> = | |
& { | |
find: (method: Method, pathname: string) => TMatchedRoute<Value>; | |
} | |
& { | |
[k in Lowercase<Method>]: ( | |
pathname: string, | |
...values: Value[] | |
) => TRouter<Value, Method>; | |
}; | |
type TRoute<H, Method> = [Method, URLPattern, H[]]; | |
type TMatchedRoute<Value> = | |
| (URLPatternComponentResult & { handlers: Value[] }) | |
| undefined; | |
export function createRouter< | |
Value = never, | |
Method extends string = "ALL" | "GET" | "POST" | "PUT" | "PATCH" | "DELETE", | |
>( | |
init: [Method, string, Value[]][] = [], | |
) { | |
const add = ( | |
...[method, pathname, handlers]: typeof init[number] | |
): TRoute<Value, Method> => [ | |
method, | |
new URLPattern({ pathname }), | |
handlers, | |
], | |
routes: TRoute<Value, Method>[] = init.length > 0 | |
? [...init.map((i) => add(...i))] | |
: [], | |
find = ( | |
method: Method, | |
pathname: string, | |
): TMatchedRoute<Value> => { | |
let result: TMatchedRoute<Value>; | |
for (const [rmethod, pattern, handlers] of routes) { | |
if (method === rmethod || "ALL" === rmethod) { | |
const match = pattern.exec({ pathname })?.pathname; | |
if (match) { | |
if (result) { | |
result.handlers.push(...handlers); | |
} else { | |
result = { ...match, handlers }; | |
} | |
} | |
} | |
} | |
return result; | |
}, | |
get = ( | |
_: never, | |
prop: string, | |
receiver: never, | |
) => ("find" === prop | |
? find | |
: (pathname: string, ...handlers: Value[]) => | |
routes.push(add(prop as never, pathname, handlers)) && receiver); | |
return new Proxy({}, { get }) as never as TRouter<Value, Method>; | |
} |
This file contains 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
// deno-lint-ignore-file | |
import { createRouter } from "./router.ts"; | |
// deno-lint-ignore-file no-explicit-any | |
type RHandler = ( | |
req: Request, | |
params: Record<string, string | number>, | |
) => Response | Promise<Response> | void | Promise<void>; | |
const router = createRouter<RHandler>([ | |
// Initial | |
["GET", "/", [ | |
() => { | |
console.log("Step 1"); | |
}, | |
async () => { | |
console.log("Step 2"); | |
}, | |
async () => { | |
return new Response("Holaaaa") | |
}, | |
() => { | |
console.log("Not Called"); | |
}, | |
]], | |
]); | |
// you can do | |
router.get("/:hello", (req, params) => new Response("Hello "+params.hello)) | |
// sample request resolver | |
const resolveRouterResult = async (req: Request) => { | |
const { pathname } = new URL(req.url); | |
const result = router.find(req.method as never, pathname); | |
if (result) { | |
for (const handler of result.handlers) { | |
let res = await handler(req, result.groups) | |
if (res !== undefined) { | |
console.log(await res.text()); | |
break; | |
} | |
} | |
} else { | |
throw new Error("No route are found!"); | |
} | |
}; | |
await resolveRouterResult(new Request("https://arisris.com")); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment