Skip to content

Instantly share code, notes, and snippets.

@hharzer
Forked from PierBover/index.js
Created August 5, 2020 05:39
Show Gist options
  • Save hharzer/2d05281855f17e735ac4f764268e1b0c to your computer and use it in GitHub Desktop.
Save hharzer/2d05281855f17e735ac4f764268e1b0c 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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment