Error Handler
Is there a way to define an error handler? My handler may throw an exception and it would be nice to catch the exceptions from the handlers and handle them together.
Hey @davidbarratt - it's possible to do that, although a bit verbose (I might add a convenience wrapper for this in the future 😄)
Here's a full example (Cloudflare Worker, TypeScript) how one might do NotFound and error handling with the router:
import CloudflareWorkerGlobalScope from "types-cloudflare-worker"
declare var self: CloudflareWorkerGlobalScope
import { HandlerContext, Router } from "service-worker-router"
const ping = async () => new Response("pong")
const notFound = async () => new Response("Not found :(", { status: 404 })
const errorResponse = async (err: Error) => new Response(`Error occured: ${err.message}`)
const conditionalThrow = async ({ request, params }: HandlerContext): Promise<Response> => {
if (params.wish === "yes") {
throw new Error("I threw.")
}
return new Response("All good.", { status: 200 })
}
const router = new Router()
router.all("/_ping", ping)
router.all("/throw/:wish", conditionalThrow)
async function fetchEventHandler(event: FetchEvent): Promise<Response> {
try {
const match = router.handleRequest(event.request)
if (match) {
return await match.handlerPromise // await is important here for error catching to work
}
return notFound()
} catch (err) {
return errorResponse(err)
}
}
self.addEventListener("fetch", (event: FetchEvent) => {
event.respondWith(fetchEventHandler(event))
})
Results in:
❯ curl 'https://my-worker.com/_ping'
pong
❯ curl 'https://my-worker.com/foobar'
Not found :(
❯ curl 'https://my-worker.com/throw/nope'
All good.
❯ curl 'https://my-worker.com/throw/yes'
Error occured: I threw.
Hi,
I've been using this router and have had a similar wish: being able to easily handle errors, here the solution I came up with:
// withErrorHandler.js
/**
* Higher order function providing generic error handling for request handlers
* @param {Function} func a request handler
* @returns {Response}
*/
export function withErrorHandler (func) {
return async function handler (...args) {
try {
// await is not superfluous here as otherwise the catch is bypassed
return await func(...args)
} catch (error) {
const url = new URL(args.request.url)
const debug = url.searchParams.get('debug') === 'true'
return debug // show the stack trace, could be used to send it to a monitoring system or else
? new Response(error.stack || error)
: new Response(null, { status: 500 })
}
}
}
// index.js
import { Router } from 'service-worker-router'
import { routeHandler } from './routeHandler'
import { withErrorHandler } from './withErrorHandler'
const router = new Router()
router.get('/path/to/handle', withErrorHandler(routeHandler))
addEventListener('fetch', (event) => {
// Will test event.request against the defined routes
// and use event.respondWith(handler) when a route matches
return router.handleEvent(event)
})
Just sharing it in case it may help someone else. Note that this version doesn't handle 404's in case the router doesn't match any route.