Skip to content

Instantly share code, notes, and snippets.

@PierBover
Last active February 9, 2022 09:43
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save PierBover/4da745b601d30a6bb850c297b0c0c579 to your computer and use it in GitHub Desktop.
Save PierBover/4da745b601d30a6bb850c297b0c0c579 to your computer and use it in GitHub Desktop.
Micro router for Cloudflare Workers
const router = require('./router');
router.setRoutes([
{
path: '/ping',
method: 'GET',
handler: ping
},
{
path: '/hello/:name',
method: 'GET',
handler: sayHello
},
]);
addEventListener('fetch', event => {
event.respondWith(router.onRequest(event))
});
function ping() {
return new Response('PONG!', {
status: 200,
});
}
function sayHello (request, params) {
return new Response('Hello ' + params.name, {
status: 200,
});
}
const router = {
routes: []
};
router.onRequest = async (event) => {
try {
const match = matchRoute(event.request, router.routes);
if (match) {
return await match.route.handler(event.request, match.params);
} else {
return new Response('No soup for you!', {
status: 404,
});
}
} catch (error) {
return new Response(error.toString(), {
status: 500,
});
}
}
// adds the routes config to the router
router.setRoutes = (routes) => {
router.routes = routes.map((route) => {
// we create the path components of the routes in advance so these are cached and doesn't have to be computed on each request
route.pathComponents = route.path.split('/').map((component) => {
const isParam = component.startsWith(':');
const isOptional = component.startsWith('?');
const paramName = isParam || isOptional ? component.substr(1) : null;
return {
string: component,
isParam,
isOptional,
paramName
}
});
return route;
});
}
// matches a request with a route and returns a match object
function matchRoute (request, routes) {
const url = new URL(request.url);
const requestPath = url.pathname;
for (const route of routes) {
if (route.method.toLowerCase() === request.method.toLowerCase()) {
const params = matchPath(route, requestPath);
if (params) {
return {
route,
params
};
}
}
}
}
// matches paths and returns an object with the values of the parameters
function matchPath (route, requestPath) {
// If it's identical we simply return an empty object because there can be no params
if (route.path === requestPath) return {};
// We compare components of the two paths.
// We've already created the components of the router path when the worker was initialized
const requestPathComponents = requestPath.split('/');
const params = {};
for (var i = 0; i < route.pathComponents.length; i++) {
const component = route.pathComponents[i];
if (component.string !== requestPathComponents[i]) {
// TODO: implement optional parameters
if (component.isParam) {
params[component.paramName] = requestPathComponents[i];
} else {
// if one component doesn't match and it's not a param then we return undefined and stop matching this path
return;
}
}
}
return params;
}
module.exports = router;
@PierBover
Copy link
Author

Super small and efficient. No Regex that consumes precious CPU time.

Needs a bit more work but if you only need simple routes with params should be fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment