From golang-boost
Guides stacking of recovery, logger, and publisher middleware in Go event-driven services using the xgodev/boost bootstrap framework.
How this skill is triggered — by the user, by Claude, or both
Slash command
/golang-boost:boost-bootstrap-middlewareThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
**REQUIRED BACKGROUND:**
REQUIRED BACKGROUND:
boost-bootstrap-function — generic typing rule (T = *cloudevents.Event).boost-model-errors — error types matched by deadletter mode.boost-wrapper-publisher — provides the publisher consumed by the publisher middleware.recovery → logger → publisher
import (
rm "github.com/xgodev/boost/bootstrap/function/middleware/recovery"
lm "github.com/xgodev/boost/bootstrap/function/middleware/logger"
pm "github.com/xgodev/boost/bootstrap/function/middleware/publisher"
)
rec := rm.NewRecovery[*cloudevents.Event]()
lmi, _ := lm.NewAnyErrorMiddleware[*cloudevents.Event]()
pmi, _ := pm.NewAnyErrorMiddleware[*cloudevents.Event](pub)
// fn.Run path:
fn, _ := function.New[*cloudevents.Event](rec, lmi, pmi)
// Workaround path (see boost-bootstrap-adapter-pubsub):
wrp := middleware.NewAnyErrorWrapper[*cloudevents.Event](ctx, "bootstrap", rec, lmi, pmi)
| Layer | Position | Reason |
|---|---|---|
recovery | Outermost | A panic in the handler must not kill the worker before the logger gets the error |
logger | Middle | Sees both raw handler errors AND post-recovery errors; structured fields propagate |
publisher | Innermost | Fires on the handler's successful result; with deadletter config, also routes errors by type |
Reordering is a footgun: putting recovery innermost means a panic short-circuits before the logger; putting publisher outermost means it fires before the handler ran.
The publisher middleware in deadletter mode matches on the unwrapped error type name:
// Routed to a "notvalid" deadletter topic; not retried
return nil, bootsterrors.Wrap(err, bootsterrors.NotValidf("invalid event data"))
// Routed to retry / alerting; transient
return nil, bootsterrors.Wrap(err, bootsterrors.Internalf("downstream call failed"))
fmt.Errorf("%w", err) defeats this — the matcher cannot recover the type name. Always use bootsterrors.Wrap (see boost-model-errors).
ignore_errors middlewareUse only when you genuinely want a category of errors to silently ack the message instead of nack/retry. Stack outside the publisher (so the publisher still fires for non-ignored errors), but inside recovery (so panics still recover):
rec := rm.NewRecovery[*cloudevents.Event]()
imi, _ := im.NewAnyErrorMiddleware[*cloudevents.Event]() // ignore_errors
lmi, _ := lm.NewAnyErrorMiddleware[*cloudevents.Event]()
pmi, _ := pm.NewAnyErrorMiddleware[*cloudevents.Event](pub)
fn, _ := function.New[*cloudevents.Event](rec, imi, lmi, pmi)
| Red flag | Fix |
|---|---|
Chain ordered publisher → logger → recovery (or any permutation that violates outermost=recovery) | Reorder to recovery → logger → publisher |
Forgetting recovery middleware | Always include it — production functions die otherwise |
fmt.Errorf("%w", err) from a handler returned through this chain | bootsterrors.Wrap(err, bootsterrors.<Type>(...)) |
Mixing T = cloudevents.Event and T = *cloudevents.Event across middlewares | All on *cloudevents.Event |
npx claudepluginhub xgodev/boost --plugin golang-boostComposes a generic middleware chain using NewAnyErrorWrapper[T] for the Pub/Sub adapter ctx-loss workaround. Covers middleware order and manual chain building outside fn.Run.
Provides idiomatic Go HTTP middleware for context propagation, structured slog logging, error handling, and panic recovery. Use when writing middleware, adding request tracing, or cross-cutting concerns.
Idiomatic Go error handling — creation, wrapping with %w, errors.Is/As/Join, custom types, sentinels, panic/recover, slog logging, HTTP middleware, and samber/oops for production errors. Apply when creating, wrapping, inspecting, or logging errors in Go codebases.