CORE CONCEPTS
Hooks
Hooks let you observe (or lightly react to) the request lifecycle without adding another layer to the middleware chain. They're a lightweight pub/sub registry, exposed on app.hooks.
Available Hooks
app.hooks.on('afterResponse', (ctx, response) => { metrics.record(ctx.request.method, response.status); }); app.hooks.on('onError', (error, ctx) => { errorTracker.capture(error, { url: ctx.request.url }); });
Hooks vs. Middleware
Reach for a hook when you're purely observing (metrics, logging, error tracking) and don't need to touch the request/response. Reach for middleware when you need to short-circuit the chain, mutate the request/response, or run code between specific other middleware.
Hooks also can't produce a Response — they're fire-and-forget observers, not part of the response pipeline.
Error Isolation
If a hook handler throws, Futon swallows the error and continues running the remaining handlers for that hook — a broken metrics call can't crash a request. Handle your own errors inside the hook if you need to know about failures.
app.hooks.on('afterResponse', (ctx, response) => { /* handler A */ }); app.hooks.on('afterResponse', (ctx, response) => { /* handler B still runs even if A throws */ });
Removing a Hook
const handler = (ctx) => console.log('request started'); app.hooks.on('beforeRequest', handler); app.hooks.off('beforeRequest', handler);
Zero-cost When Unused
HookSystem.has(name) reports whether any handler is registered for a given hook. Futon's fetch() guards every emit() call with has() first, so apps that never register hooks skip the async dispatch overhead entirely.
