httpmw

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package httpmw provides shared HTTP middleware for paper-board services.

Canonical chain order (top-of-stack first), per docs/standards/http-api-conventions.md §2:

  1. RequestID — read X-Request-Id or mint UUIDv7
  2. TrustHeaders — Phase 7+ from gateway; pre-Phase-7 acts as a no-op
  3. OtelHTTP — otelhttp.NewHandler wrapper
  4. Recover — panic-to-500 with structured log
  5. Logger — start/end log with route/status/duration
  6. BodyLimit — http.MaxBytesReader
  7. AuthStub | Auth — Phase 1: AuthStub stamps DefaultOrgID; Phase 2: real Auth
  8. (chi.Compress) — gzip (provided by chi)
  9. Timeout — request-scoped deadline (chi.Middleware.Timeout)

HandleErr is the central HTTP error boundary; every handler converts a service-level error into the canonical envelope via this function.

Index

Constants

View Source
const (
	HeaderOrgID  = "X-Org-Id"
	HeaderUserID = "X-User-Id"
	HeaderRoles  = "X-Roles"
)

Gateway-issued auth header names (Phase 7+).

View Source
const HeaderRequestID = "X-Request-Id"

HeaderRequestID is the canonical header name for inbound + outbound request IDs.

Variables

This section is empty.

Functions

func AuthStub

func AuthStub(orgID uuid.UUID) func(http.Handler) http.Handler

AuthStub stamps a hardcoded org_id into the request ctx. Used in Phase 1.x before identity Phase 2 ships. Phase 2 swaps this for real Auth that validates JWT/API keys and stamps real org_id/user_id/roles.

SECURITY: AuthStub bypasses authentication entirely. Use only behind a trusted boundary (Phase 1.x dev cluster + private network). Phase 2 onward MUST replace it with Auth.

func BodyLimit

func BodyLimit(n int64) func(http.Handler) http.Handler

BodyLimit caps the inbound request body at n bytes via http.MaxBytesReader. Use 1<<20 (1 MiB) for JSON; raise for endpoints accepting file uploads. Streaming routes (SSE, chunked uploads) should opt out via chi.Router.With.

func HandleErr

func HandleErr(w http.ResponseWriter, r *http.Request, svc string, err error)

HandleErr writes the canonical error envelope and logs the wrapped chain at the HTTP boundary. Code format: "<svc>.<short>" where short comes from errors.Kind (not_found, conflict, ..., internal).

Message is the bare sentinel string (e.g. "not found"). The wrapped detail is NOT echoed to clients — full chain goes only to logs via log.ErrorAttrs.

If err wraps validator.ValidationErrors, individual field failures appear under details with HTTP 400 + invalid_input code.

func Logger

func Logger(next http.Handler) http.Handler

Logger emits one INFO record per request with the canonical observability keys: method, route, status, duration_ms, bytes_in, bytes_out. Place after RequestID + Recover so request_id is in ctx and panics are caught first.

func OtelHTTP

func OtelHTTP(serviceName string) func(http.Handler) http.Handler

OtelHTTP returns a middleware wrapping next with otelhttp.NewHandler. The service name names the operation; per-route span name refinement happens downstream via chi route patterns.

func Recover

func Recover(svc string) func(http.Handler) http.Handler

Recover catches panics from downstream handlers, logs structured (panic value + stack + error.kind=panic), and writes the canonical 500 envelope via HandleErr. Service name names the error code suffix.

func RequestID

func RequestID(next http.Handler) http.Handler

RequestID reads HeaderRequestID from the inbound request; mints a UUIDv7 if absent. The value is stored in ctx via log.WithRequestID and echoed back on the response header so clients can correlate.

func TrustHeaders

func TrustHeaders(next http.Handler) http.Handler

TrustHeaders reads gateway-issued auth headers and writes them into the request ctx. Used Phase 7+ when the gateway terminates auth and downstream services trust headers. Pre-Phase-7, headers are usually empty so this is a silent no-op.

SECURITY: enable only when fronted by an authenticated gateway. Direct internet exposure with TrustHeaders enabled is a confused-deputy risk — any caller can claim any org/user.

func WriteError

func WriteError(w http.ResponseWriter, status int, code, message string)

WriteError emits the canonical envelope without log. For low-level paths where logging happened earlier or is intentionally skipped (e.g. healthz).

Types

type ErrorBody

type ErrorBody struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	Details any    `json:"details,omitempty"`
}

ErrorBody is the payload inside ErrorEnvelope.

type ErrorEnvelope

type ErrorEnvelope struct {
	Error ErrorBody `json:"error"`
}

ErrorEnvelope is the canonical JSON wire shape for HTTP error responses.

type ValidationDetail

type ValidationDetail struct {
	Field   string `json:"field"`
	Rule    string `json:"rule"`
	Message string `json:"message,omitempty"`
}

ValidationDetail describes a single validator/v10 field failure surfaced in ErrorBody.Details when an HTTP boundary is handed a validator.ValidationErrors.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL