This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Thomas Reggi
The new set of standard classes, such as URLPattern, Response, Request, and URL, has made me ponder what native Route/Router classes would look like. I've experimented with various server frameworks like Express, Oak, and Fresh, and have been contemplating what a server would look like from the perspective of WinterCG or Mozilla. Here's a simple Route and Routes class that only matches on method and URL.
type Handler = (req: Request) => Promise<Response> | Response
export class Route {
urlPattern: URLPattern
constructor (
public method: string,
public pathname: string,
public handler: Handler
) {
this.urlPattern = new URLPattern({ pathname })
}
static get (pathname: string, handler: Handler) {
return new Route("GET", pathname, handler)
}
}
export class Routes {
routes: Route[]
constructor (...routes: Route[]) {
this.routes = routes
}
async match (req: Request): Promise<Response> {
const matches = this.routes.filter(route => {
return route.urlPattern.test(req.url) && route.method === req.method
})
if (matches.length > 1) {
return new Response('internal error, more then one route with the same pathname')
} else if (matches.length === 0) {
return new Response('internal error, not found')
} else {
const response = await matches[0].handler(req)
return response
}
}
}
This would allow you to do something like:
import { serve } from "https://deno.land/std@0.173.0/http/server.ts";
const routes = new Routes(
Route.get('/elephant', () => Response.json('elephant')),
Route.get('/monkey', () => Response.json('monkey')),
Route.get('/racoon', () => Response.json('racoon')),
Route.get('/bunny', () => Response.json('bunny')),
)
// To listen on port 4242.
serve((request) => {
return routes.match(request)
}, { port: 4242 });
This has one issue: what if you want to match on other parts of the Request? This can become complicated quickly. Along with the standard URL and method, there are the header, query, body, and should a matcher match on the extracted, parsed body? If so, should the body also be validated within the Request object? To access the body, it is behind a promise. To match the body, you would need to know the type of body (e.g. form, XML, JSON) and then use a tool like zod to validate it. It's not outlined here but I had created the idea of WrapedRequest
that would cache the body within the instance of the WrapedRequest
so that the promise is resolved once and passed to every Router
for matching.
A more complex Route
class could have all the matching criteria listed out as an array of options and requirements.
new RouteMatcher(
method.get,
oneOf(urlPattern('/elephant'), urlPattern('/elephant/:name')),
oneOf([queryParam('name'), urlPattern('/elephant/:name'), header('x-name')]),
)
I like the concept of a simple Route/Router that can match against various criteria, and I wonder if others are also contemplating this idea. It certainly simplifies things to only match against the method and URL path.
I have been pondering the potential for a native Route/Router class that can match against a variety of criteria. I have been experimenting with different server frameworks and considering the design of a server from various perspectives. I propose that a simple Route and Routes class that can match on method and URL is a good starting point, but there is room for expansion to match on other criteria as well. I am curious to hear if others share this perspective and would like to open a discussion on this topic.
This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Thomas Reggi
Thomas Reggi | Sciencx (2023-01-23T01:24:02+00:00) A Native JavaScript Route / Router Proposal. Retrieved from https://www.scien.cx/2023/01/23/a-native-javascript-route-router-proposal/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.