Files
httpserver/docs/adr/ADR-002-embedded-chi-router.md
Rene Nochebuena 1ec0780f72 docs(httpserver): correct tier from 4 to 3
httpserver depends on launcher (Tier 2), placing it at Tier 3.
With launcher corrected from Tier 5 to Tier 2, httpserver's tier
drops accordingly.
2026-03-19 13:39:19 +00:00

2.8 KiB

ADR-002: Embedded chi.Router in HttpServerComponent

Status: Accepted Date: 2026-03-18

Context

HttpServerComponent must expose routing methods to callers so they can register routes before the server starts. There are two design options:

  1. Wrapper methods — define Get(pattern, handler), Post(...), Route(...), Mount(...), Use(...), etc. on HttpServerComponent explicitly, delegating each to an internal router.
  2. Embedded router — embed chi.Router directly in the interface and the concrete struct, so all chi routing methods are promoted to the component surface automatically.

The application bootstrap pattern in this project registers routes in a lc.BeforeStart(...) hook, after db.OnInit has run but before srv.OnStart binds the port. This means the same value returned by httpserver.New(...) is used both as a launcher.Component (lifecycle) and as a router (route registration). Callers write:

srv := httpserver.New(logger, cfg, httpserver.WithMiddleware(...))
lc.Append(db, srv)
lc.BeforeStart(func() error {
    srv.Get("/health", healthHandler)
    srv.Route("/api/v1", func(r chi.Router) { ... })
    return nil
})

Decision

Embed chi.Router directly in both the HttpServerComponent interface and the httpServer concrete struct. The interface is declared as:

type HttpServerComponent interface {
    launcher.Component
    chi.Router
}

The struct embeds chi.Router as an anonymous field, initialized to chi.NewRouter() in New().

This means callers receive the complete chi routing API — Get, Post, Put, Delete, Patch, Head, Options, Route, Mount, Use, With, Group, Handle, HandleFunc, Method, MethodFunc, Connect, Trace, NotFound, MethodNotAllowed — without any wrapper boilerplate and without any risk of an incomplete wrapper missing a method.

The compliance test (compliance_test.go) asserts at compile time:

var _ launcher.Component = httpserver.New(...)
var _ chi.Router         = httpserver.New(...)

Consequences

  • chi is a visible part of the HttpServerComponent API. Callers using Route(...) or Mount(...) must import github.com/go-chi/chi/v5 for the callback type. This is intentional and explicit, not a leaky abstraction — httpserver is documented as chi-backed.
  • A future swap to a different router would be a breaking change to HttpServerComponent. This is accepted: the router choice is a deliberate, long-term decision (see ADR-001).
  • Writing wrapper methods would have to be updated every time chi adds a new method. Embedding avoids that maintenance burden permanently.
  • Middleware is applied to the embedded router in OnInit() by calling s.Router.Use(mw) for each registered middleware, preserving chi's standard middleware chaining semantics.