httpmw only depends on logz (Tier 1), placing it at Tier 2. The previous docs incorrectly stated both the module tier (3) and the logz tier (0).
httpmw
net/http middleware for transport-layer concerns: panic recovery, CORS, request ID injection, and structured request logging.
Overview
Four composable func(http.Handler) http.Handler middleware functions:
| Middleware | Responsibility |
|---|---|
Recover() |
Catches panics; writes 500; captures stack trace |
RequestID(generator) |
Generates a unique ID; injects it via logz.WithRequestID; sets X-Request-ID header |
RequestLogger(logger) |
Logs method, path, status, latency, and request ID after each request |
CORS(origins) |
Sets CORS headers; short-circuits OPTIONS with 204 |
No authentication or identity logic lives here — see httpauth-firebase for that.
Install
go get code.nochebuena.dev/go/httpmw
Usage
// Recommended order — outermost middleware first
mux.Use(httpmw.Recover())
mux.Use(httpmw.RequestID(uuid.NewString))
mux.Use(httpmw.RequestLogger(logger))
mux.Use(httpmw.CORS([]string{"https://example.com"}))
Pass []string{"*"} to CORS to allow any origin (development only).
Middleware
Recover
mux.Use(httpmw.Recover())
Wraps the handler in a defer/recover. On panic, writes 500 Internal Server Error and captures debug.Stack(). No logger is required.
RequestID
mux.Use(httpmw.RequestID(uuid.NewString))
Calls generator() on every request, stores the ID with logz.WithRequestID, and writes it to the X-Request-ID response header. Must run before RequestLogger so the ID is in context when the logger reads it.
RequestLogger
mux.Use(httpmw.RequestLogger(logger))
Logs method, path, status, latency, and request_id after the inner handler returns. Uses logger.Error for 5xx responses and logger.Info for all others.
CORS
mux.Use(httpmw.CORS([]string{"https://app.example.com", "https://admin.example.com"}))
Sets Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers for matching origins. OPTIONS requests are short-circuited with 204 No Content.
Allowed methods: GET, HEAD, PUT, PATCH, POST, DELETE, OPTIONS
Allowed headers: Content-Type, Authorization, X-Request-ID
Logger interface
RequestLogger accepts any value satisfying the Logger interface — satisfied by logz.Logger via duck typing:
type Logger interface {
Info(msg string, args ...any)
Error(msg string, err error, args ...any)
With(args ...any) Logger
}
StatusRecorder
StatusRecorder is exported for use in custom middleware that needs to inspect the written status code:
rec := &httpmw.StatusRecorder{ResponseWriter: w, Status: http.StatusOK}
next.ServeHTTP(rec, r)
fmt.Println(rec.Status) // e.g. 404
Dependencies
code.nochebuena.dev/go/logz