Repository navigation
Restructure the library: opt-in dependencies, one middleware, and new outbound + problemjson packages - #6
Merged
andresbott merged 16 commits intoSep 11, 2026
Conversation
…, register-error test)
…dotfile block, dev upstream reason)
andresbott
deleted the
refactor/middleware-remove-standalone-wrappers
branch
September 11, 2026 22:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
middlewarepackage hard-depended on Prometheus and a concrete*slog.Logger— so every consumer compiled Prometheus whether they used it or not — and it shipped standalone wrappers that duplicated the combined middleware while baking an ad-hoc JSON error shape into the request path. This reshapes the library around interface seams so a package's third-party dependencies are pulled in only when you import it, collapses the several middlewares into one handler that never rewrites response bodies, and broadens the scope from inbound-only to inbound + outbound.What
middlewaredepends on newObserver(metrics) andLoggerinterfaces instead of concrete types; Prometheus moves to a newmiddleware/metricspackage (NewObserver/NopObserver) andgolang.org/x/time/ratelives only inoutbound. Importingmiddlewarealone links no third-party code — and*slog.Loggerstill satisfiesLogger, so existing callers pass it unchanged.Logging,Metrics,JSONErrors,GenericErrors, andPanicRecoverstandalone functions; the combinedMiddleware(method renamedMiddleware()→Wrap()) is the sole entry point and only logs, records metrics, and recovers panics. It no longer modifies the body — error bodies are teed to the client and captured for logging.ReqDelaystays as the one dev-only helper.outboundpackage for a service's own calls to third-party APIs: a throttled, retrying client (rate limiting, bounded exponential backoff for 5xx/429/timeouts,Retry-Aftercompliance) with a typed*Errorcarrying a failureKind, HTTP status, and user-facing message.problemjsonpackage writing RFC 9457application/problem+json, replacing the removed{"error":...}shape.ModeMaskedstrips detail in production;WriteUpstreamrenders anoutboundfailure through a structural interface, so neither package imports the other.Cfg-struct constructors acrossmiddleware.New,metrics.NewObserver,spa.New,outbound.New, andproblemjson.New.spamoves fromhandlers/spa/to top-levelspa/and gains dotfile blocking (except.well-known/) plusPathPrefixnormalisation; the oldlib/limitiopackage is inlined intomiddlewareas an unexported capped buffer."unmatched", normalised method — the raw request path is never a label); panic recovery no longer synthesises a 500 over a response the handler already started (newStarted()guard, matching net/http); the log correlation id is configurable viaCfg.RequestID;ReqDelayhonours the request context.Notes
!commits) — no compatibility shims were kept; callers move to the newCfgconstructors andWrap.problemjsonpackage. Running a body-rewriting middleware alongsideproblemjsonwould double-encode, so a route group should use one or the other.CLAUDE.mdbecomesAGENTS.md(a compat symlink is kept), which documents the target architecture;go.modmoves to Go 1.25.