Documentation
¶
Overview ¶
Package errormap provides middleware that translates errors returned by handlers into HTTP problems.
Arkoren's own errors that carry HTTP meaning are already problem.Details. Domain and contract sentinels such as database.ErrNoRows or storage.ErrNotFound are plain errors, so the router renders them as 500. errormap is where an application decides what they mean over HTTP:
r.Use(errormap.Middleware( errormap.Map(database.ErrNoRows, problem.NotFound()), errormap.Map(storage.ErrNotFound, problem.NotFound()), errormap.Func(request.ValidationProblem), ))
Mappings run in order and the first one that handles the error wins. Map keeps the original error in the replacement's chain, so logging and tracing still see the cause; Func mappings own their cause preservation. A mapping that returns nil or its input unchanged, such as request.ValidationProblem for other errors, passes the error on to the next one.
Unchanged detection compares values and retained references, not error categories. A derived problem with different detail, headers, or cause is handled even when errors.Is matches the input. See Mapping for limits of detecting unchanged value errors; returning nil always declines a mapping. Middleware copies the ordered mapping slice at construction, but functions and their captured values remain shared.
Placement ¶
Register errormap first with Use, so it wraps every later middleware and sees the errors they return. Errors the router produces for unmatched routes (404) and wrong methods (405) pass through router-wide middleware, including this mapper. Middleware attached only to a matched group or route does not wrap unmatched requests on the parent router.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Middleware ¶
func Middleware(mappings ...Mapping) router.Middleware
Middleware returns middleware that maps errors returned by the next handler. Mappings are tried in the order given and the first one that returns an error other than nil or its unchanged input wins. Each mapping receives the original handler error, not a previous mapping's result. An unhandled error and a nil error pass through unchanged. Errors are only mapped when the handler returns them; a response already written is left alone.
The ordered mapping slice is copied at construction; functions and their captured values remain shared. See Mapping for unchanged-value semantics.
Register it first with Use so it sees the errors of every later middleware, including router-generated 404 and 405 problems at router-wide scope. Calling Middleware with no mappings is a no-op.
Example:
r.Use(errormap.Middleware( errormap.Map(database.ErrNoRows, problem.NotFound()), ))
Types ¶
type Mapping ¶
Mapping translates an error into another error. It returns nil, or its input unchanged, when it does not handle the error, so Middleware tries the next mapping. Build one with Map or Func. Matching an input with errors.Is does not make a replacement unchanged.
Comparable errors use equality. Non-comparable values compare their fields and elements recursively, with maps and slices compared by storage identity rather than contents. Separately constructed value errors with equal fields are indistinguishable, and non-nil functions and NaNs cannot compare equal. Return nil to decline reliably for those cases, or use pointer errors when replacement identity matters. Mappings must not mutate their input; detection does not snapshot errors.
Example:
mapping := errormap.Map(database.ErrNoRows, problem.NotFound()) r.Use(errormap.Middleware(mapping))
func Func ¶
Func returns a Mapping backed by fn, for conversions that need to inspect the error, such as extracting a typed error with errors.As. fn returns the replacement error, or nil or the unchanged input to leave the error unhandled and try the next mapping. See Mapping for unchanged-value comparison limits. fn owns cause preservation for its replacement; unlike Map, Func does not attach the original error automatically.
The signature matches request.ValidationProblem, so errormap.Func(request.ValidationProblem) converts validate.Errors to a 422 problem and passes every other error on, in any position.
Example:
errormap.Func(func(err error) error {
if conflict, ok := errors.AsType[*ConflictError](err); ok {
return problem.Conflict().Derive(func(details *problem.Builder) {
details.Detail(conflict.Reason)
details.Error(err)
})
}
return nil
})
func Map ¶
Map returns a Mapping that replaces any error matching target, according to errors.Is, with replacement. The replacement wraps the original error (problem.Builder.Error in problem.Details.Derive), so the cause stays available to logging and tracing and still matches target with errors.Is.
replacement is a problem.Details rather than an error because attaching the cause uses a derivation callback; any other error type could not carry it.
Example:
errormap.Map(database.ErrNoRows, problem.NotFound().Derive(func(details *problem.Builder) {
details.Detail("No such record.")
}))