Help us improve
Share bugs, ideas, or general feedback.
From golang-boost
Guides stacking function middleware in Go event-driven services using boost bootstrap packages. Covers recovery->logger->publisher chain order, error wrapping for deadletter routing, and ignore_errors middleware.
npx claudepluginhub xgodev/boost --plugin golang-boostHow 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:**
Composes a generic middleware chain outside fn.Run presets, typically for the Pub/Sub adapter ctx-loss workaround. Covers NewAnyErrorWrapper[T] and manual chain building.
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.
Guides idiomatic Go error handling: wrapping with %w, sentinel errors, errors.Is/As, errors.Join, custom types, panic/recover, and structured logging with slog. Useful when creating, inspecting, or logging errors.
Share bugs, ideas, or general feedback.
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 |