# ADR-003: No Middleware Installed by Default **Status:** Accepted **Date:** 2026-03-18 ## Context Many HTTP frameworks and server libraries install a default middleware stack — request logging, panic recovery, CORS headers, request ID injection — on the assumption that most applications will want these. The question is whether `httpserver.New(...)` should do the same. Arguments for a default stack: - Reduces boilerplate for the common case. - Ensures recovery from panics is always in place. Arguments against: - Different applications need different middleware in different orders. Order matters: request ID must precede the logger so the logger can attach the ID; auth must precede RBAC; compression must follow auth. - Installing middleware that the caller neither requested nor knows about makes the system harder to reason about and test. - The micro-lib design principle is zero-surprise construction: `New()` produces a minimal, predictable value. Behavior is added explicitly. - A middleware like `RequestLogger` needs a logger argument that `httpserver` would have to accept on behalf of `httpmw`. This couples configuration surface areas that should be independent. ## Decision `httpserver.New(...)` installs no middleware. The router returned from `New()` is a bare `chi.NewRouter()`. Middleware is composed explicitly by the caller using `WithMiddleware(...)`: ```go srv := httpserver.New(logger, cfg, httpserver.WithMiddleware( httpmw.RequestID(uuid.NewString), httpmw.Recover(), httpmw.RequestLogger(&logAdapter{logger}), ), ) ``` `WithMiddleware` is a variadic functional option that appends middleware to an internal slice. In `OnInit()`, each middleware is applied to the router via `s.Router.Use(mw)` in registration order (first registered = outermost in the chain). ## Consequences - Every application must explicitly compose its middleware stack. This is intentional: the stack is visible, ordered, and testable in the application source. - Panic recovery is not automatic. If a caller omits `httpmw.Recover()`, unhandled panics will crash the process. This is a deliberate trade-off — it makes the stack explicit and avoids silent behavior. - Adding middleware after `OnInit()` has run is permitted by chi but is not the intended usage pattern. Route registration and middleware composition should both happen in the `BeforeStart` hook (before `OnStart` binds the port). - The `WithMiddleware` option appends, so multiple calls accumulate: `WithMiddleware(a, b)` followed by `WithMiddleware(c)` results in `[a, b, c]` applied in that order.