# ADR 0004 — Four lifecycle hooks instead of a middleware chain

> Source: https://github.com/max-listov/stitchkit/blob/master/docs/decisions/0004-lifecycle-hooks.md

---

# ADR 0004 — Four lifecycle hooks instead of a middleware chain

- **Status:** Accepted
- **Date:** 2025-05

## Context

An earlier framework used Hono's per-path `.use()` middleware: a registration
step mounted middleware by scope, and request behaviour was assembled from a
chain of middleware functions. That chain is a framework-specific construct —
adopting it means adopting the framework (see ADR 0001).

stitchkit still needs the cross-cutting behaviour middleware usually provides:
logging, auth, response shaping, error formatting.

## Decision

Four lifecycle hooks, plain functions, no chain:

- **`onRequest(req)`** — runs first; logging, rate limiting. May short-circuit
  with a `Response`.
- **`beforeHandle(ctx, endpoint)`** — auth, scope checks. Runs after the context
  is built, before the handler.
- **`afterHandle(ctx, result, endpoint)`** — response transform, cache headers,
  audit logging. May replace the result.
- **`onError(ctx, error, endpoint)`** — error formatting, alerting.

Route groups (ADR 0006) may carry their own hooks. Execution order is
global hook → group hook → handler.

## Consequences

- Hooks are ordinary functions with zero framework dependency — the opposite of
  middleware lock-in.
- The four hooks cover every cross-cutting concern the source projects needed.
- There is no composable middleware *pipeline* — hooks are flat, not chained.
  Behaviour that a middleware stack would compose is instead expressed as plain
  code inside a hook. For stitchkit's scope this is simpler, not weaker.
