CORE CONCEPTS
Error Handling
Futon gives you a small HttpError class hierarchy for representing HTTP-status-bearing errors, plus two hooks — app.onError() and app.notFound() — for turning uncaught exceptions and unmatched routes into responses.
The Error Hierarchy
import { HttpError, NotFoundError, MethodNotAllowedError, InternalServerError, } from '@rasenganjs/futon'; throw new NotFoundError('User not found'); throw new HttpError(422, 'Validation failed');
Every HttpError carries a numeric .status property.
Futon does not automatically turn a thrown HttpError into a response with
the matching status code — your onError handler needs to check error instanceof HttpError and use error.status yourself. Without an onError
handler, every thrown error (including HttpErrors) becomes a generic 500
response.
import { json, HttpError } from '@rasenganjs/futon'; app.onError(async (error, ctx) => { if (error instanceof HttpError) { return json({ error: error.message }, { status: error.status }); } console.error(error); return json({ error: 'Internal Server Error' }, { status: 500 }); });
app.onError()
Registers a global handler for any error that escapes the middleware chain — thrown from a handler, a middleware, or the router itself:
app.onError(async (error, ctx) => { console.error(`[${ctx.request.method}] ${ctx.request.url}`, error); return json({ error: error.message }, { status: 500 }); });
Without an onError handler registered, Futon falls back to a plain-text 500 Internal Server Error response containing the error's message.
app.notFound()
Registers a handler for requests that don't match any registered route:
app.notFound(async (ctx) => { return json( { error: `No route for ${ctx.request.method} ${ctx.request.url}` }, { status: 404 } ); });
Futon enforces the 404 status on the response even if your handler forgets to set it — if your notFoundHandler returns a 200, Futon rewrites it to 404 while preserving the body and headers. Without a custom handler, Futon returns a plain-text "Not Found" response.
Order of Operations
On every request, Futon:
- Emits the
beforeRequesthook (see Hooks). - Runs the composed middleware + router chain.
- If nothing matched, falls through to
notFound(). - If anything threw, emits the
onErrorhook, then callserrorHandler()(or the default 500 fallback). - Emits the
afterResponsehook with the finalResponse.
