Documentation
¶
Overview ¶
Package web provides JSON HTTP adapter composition for net/http handlers.
Handlers return rslt.Result[Response] instead of writing to ResponseWriter, making them testable expressions that return values. Adapt bridges handlers to http.HandlerFunc at the registration boundary.
This package helps build HTTP adapters — the transport boundary layer. Domain logic should live in separate functions that handlers call. JSON request/response only — not a general web framework.
Index ¶
- func Adapt(h Handler, opts ...AdaptOption) http.HandlerFunc
- func BadRequest(msg string) error
- func Conflict(msg string) error
- func DecodeJSON[T any](req *http.Request) rslt.Result[T]
- func DecodeJSONWith[T any](req *http.Request, opts DecodeOpts) rslt.Result[T]
- func Forbidden(msg string) error
- func NotFound(msg string) error
- func PathParam(req *http.Request, name string) option.String
- func StatusError(status int, code, msg string) error
- func Steps[T any](fns ...func(T) rslt.Result[T]) func(T) rslt.Result[T]
- func TooManyRequests(msg string) error
- type AdaptOption
- type ClientError
- type DecodeOpts
- type Error
- type Handler
- type Response
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Adapt ¶
func Adapt(h Handler, opts ...AdaptOption) http.HandlerFunc
Adapt converts a Handler into an http.HandlerFunc.
Rendering: marshals Body to buffer via json.Marshal before writing any response bytes. If marshaling fails, returns 500 (no partial writes).
Error rendering flow (uses errors.As, not type assertion):
- errors.As(err, &webErr) → render *Error directly (with Headers, Details)
- Else if ErrorMapper set → call mapper
- If mapper returns (*Error, true) → render that
- Else → 500 with generic "internal error"
Panics if h is nil.
func DecodeJSON ¶
DecodeJSON reads and JSON-decodes the request body into T.
Policy:
- Content-Type: accepts application/json, application/json;charset=utf-8, and application/*+json variants. Missing Content-Type is accepted (lenient for ad hoc clients). Wrong Content-Type returns 415.
- Max body size: 1MB (returns 413 if exceeded)
- Disallows unknown fields (returns 400)
- Empty body returns 400
- Malformed JSON returns 400
- Trailing garbage after valid JSON returns 400
func DecodeJSONWith ¶
DecodeJSONWith overrides default decode policy.
func PathParam ¶ added in v0.54.0
PathParam returns the named path parameter from the request as an Option. Returns not-ok if the parameter is missing or empty. Wraps http.Request.PathValue with option.NonEmpty.
func StatusError ¶
StatusError returns an error with the given status, code, and message.
func Steps ¶
Steps chains functions in order, short-circuiting on first error. Each step may validate, normalize, or transform the value. Zero steps returns identity (rslt.Ok). Panics if any fn is nil. For aggregated validation (collecting all errors), write a single step that runs checks and returns a structured *Error with Details.
func TooManyRequests ¶ added in v0.54.0
TooManyRequests returns a 429 error.
Types ¶
type AdaptOption ¶
type AdaptOption func(*adaptConfig)
AdaptOption configures Adapt behavior. Scope is strictly response/error rendering. Cross-cutting concerns (logging, metrics, tracing, auth) belong in standard func(http.Handler) http.Handler middleware.
func WithErrorMapper ¶
func WithErrorMapper(fn func(error) (*Error, bool)) AdaptOption
WithErrorMapper maps domain errors to *Error at the adapter boundary. Only called for errors that are NOT already *Error (transport errors from DecodeJSON, validation errors that are already *Error bypass this). Return (*Error, true) to handle, or (nil, false) to fall through to 500. At most one mapper per Adapt call; last wins if called multiple times. Panics if fn is nil.
type ClientError ¶
type ClientError struct {
Error string `json:"error"`
Code string `json:"code,omitempty"`
Details any `json:"details,omitempty"`
}
ClientError is the JSON shape written to the client for all errors. Extensible via the Details field for field-level validation, rate-limit metadata, or any structured payload.
type DecodeOpts ¶
type DecodeOpts struct {
MaxBytes int64 // 0 = use default (1MB)
AllowUnknown bool // false = reject unknown fields (default)
}
DecodeOpts provides per-call overrides for decode policy.
type Error ¶
type Error struct {
Status int
Message string
Code string
Details any
Headers http.Header // optional response headers (WWW-Authenticate, Retry-After)
Err error // internal cause, never sent to client
}
Error carries an HTTP status code, client-safe message, and optional structured details. Message is what the client sees. Err (if set) is for logs and errors.Is/As only — never sent to clients.
type Handler ¶
Handler is a function from request to result. Not pure in the FP sense (*http.Request has mutable state), but more functional than ResponseWriter mutation — handlers are testable expressions that return values.
type Response ¶
type Response struct {
Status int
Headers http.Header // nil means no extra headers; copied in Adapt
Body any // JSON-serialized by Adapt; nil means no body
}
Response is the return value of a handler — status + headers + body. Body is any because a generic Response[T] would infect the entire Handler signature. This is a boundary escape hatch, not full type safety. Constructors are generic to preserve call-site type intent.