Worker Router
A router for Worker Environments such and Cloudflare Workers or Service Workers.
Work In Progress
This router is inspired by previous work such as tiny-request-router
and itty-router
, but it
improves on them by providing better support for middleware, type inference, nested routing, and broader URL matching for use in service workers.
🆓 Type Inference
The goal of Worker Router is to infer types based on usage so that no explicit typing is required for standard use cases. This allows even JavaScript users to benefit from inline documentation and API discoverability. For example,
const router = new WorkersRouter(basics())
.get('/about', (req, { userAgent }) => ok())
.get('/login', unsignedCookies(), (req, { userAgent, cookies }) => ok())
In this example your editor can infer the types and documentation for
userAgent
, provided by thebasics
middleware for the entire routercookies
, provided by theunsignedCookies
middleware for this route only
🔋 Functional Middleware
Worker Router middlewares are just function that add properties to a generic context object. As such, they can be mixed and matched using standard tools from functional programming.
For convenience, this module provides a combine
utility to combine multiple middlewares into one.
const myReusableMW = combine(
basics(),
signedCookies({ secret: 'password123' }),
cookieSession({ user: '' })
);
const router = new WorkersRouter()
.get('/', myReusableMW, () => ok())
.post('/', combine(myReusableMW, bodyParser()), () => ok())
Note that type inference is maintained when combining middleware!
🪆 Nested Routing
Worker Router supports delegating entire sub routes to another router:
const itemRouter = new WorkerRouter()
.get('/', (req, { params }) => ok(`Matched "/item/`))
.get('/:id', (req, { params }) => ok(`Matched "/item/${params.id}`))
const router = new WorkersRouter()
.get('/', () => ok('Main Page'))
.use('/item*', itemRouter)
TODO: Provide parent matches to child router...
⚙️ Ready for Service... Worker
Internally, this router uses URLPattern
for routing, which allows it match URLs in the broadest sense.
For example, the following router, meant to be used in a Service Worker, can handle internal requests as well as intercept calls to external resources:
// file: "sw.js"
const router = new WorkersRouter()
.get('/', () => ok('Main Page'))
.get('/about', () => ok('About Page'))
.external('https://plausible.io/api/*', req => {
// intercepted
})
💥 Error Handling Without Even Trying
Worker Router has first class support for error handling. Its main purpose is to let you write your handlers without having to wrap everything inside a massive try {} catch
block. Instead, you can define special recover routes that get invoked when something goes wrong.
const router = new WorkersRouter()
.get('/', () => ok('Main Page'))
.get('/about', () => { throw Error('bang') })
.recover('*', (req, { error, response }) =>
new Response(`Something went wrong: ${error.message}`, response)
);
✅ Works with Workers
Worker Router comes with out of the box support for a variety of Worker Environments:
To use it in an environment that provides a global fetch
event, use
self.addEventListener('fetch', router)
(This works because the router implements the EventListener
interface)
To use it with Cloudflare's module workers, use
export default router
(This works because the router implements a fetch
method with compatible interface)
To use it with Deno/Deploy's serve
function, use
serve(router.serveCallback)