-
Release v0.9.0 Stable
released this
2026-03-19 07:10:22 -06:00 | 0 commits to main since this releasev0.9.0
code.nochebuena.dev/go/httputilOverview
httputilremoves HTTP boilerplate from business logic. Generic adapter functions wrap
pure Go functions intohttp.HandlerFuncvalues, handling JSON decode, struct validation,
JSON encode, and error-to-HTTP-status mapping automatically. A singleErrorhelper
translates any*xerrors.Errto the correct HTTP status and JSON body.This is the initial stable release. The API has been designed through multiple architecture
reviews and validated end-to-end via the todo-api POC. It is versioned v0.9.0 rather than
v1.0.0 because it has not yet been exercised across all production edge cases, and minor
API refinements may follow.What's Included
Handler adapters (generic):
Handle[Req, Res any](v Validator, fn func(ctx, Req) (Res, error)) http.HandlerFunc
— decodes JSON body, validates, calls fn, encodes result as JSON 200HandleNoBody[Res any](fn func(ctx) (Res, error)) http.HandlerFunc
— no body decode or validation; encodes result as JSON 200 (for GET/DELETE)HandleEmpty[Req any](v Validator, fn func(ctx, Req) error) http.HandlerFunc
— decodes JSON body, validates, calls fn, returns 204 on success
Manual handler type:
HandlerFunc func(w, r) error— implementshttp.Handler; on non-nil return, callsError(w, err)
Response helpers:
JSON(w, status, v)— encodes v as JSON, sets Content-Type: application/jsonNoContent(w)— writes 204 No ContentError(w, err)— maps*xerrors.Errcodes to HTTP status codes; falls back to 500 for
unknown errors; always writes{"code": "...", "message": "..."}JSON body
xerrors.Code → HTTP status mapping (12 codes):
Code Status ErrInvalidInput400 Bad Request ErrUnauthorized401 Unauthorized ErrPermissionDenied403 Forbidden ErrNotFound404 Not Found ErrAlreadyExists409 Conflict ErrGone410 Gone ErrPreconditionFailed412 Precondition Failed ErrRateLimited429 Too Many Requests ErrInternal500 Internal Server Error ErrNotImplemented501 Not Implemented ErrUnavailable503 Service Unavailable ErrDeadlineExceeded504 Gateway Timeout Installation
go get code.nochebuena.dev/go/httputil@v0.9.0import "code.nochebuena.dev/go/httputil" // Typed handler with request and response r.Post("/items", httputil.Handle(validator, svc.CreateItem)) // Read-only handler (no request body) r.Get("/items/{id}", httputil.HandleNoBody(func(ctx context.Context) (Item, error) { return svc.GetItem(ctx, chi.URLParamFromCtx(ctx, "id")) })) // Write-only handler (no response body) r.Delete("/items/{id}", httputil.HandleEmpty(validator, svc.DeleteItem))Design Highlights
Business functions are pure Go. Functions passed to
Handle,HandleNoBody, and
HandleEmptyhave nohttp.ResponseWriteror*http.Requestin their signature. They
can be called directly in unit tests withcontext.Background()and a typed request value.Single error translation point.
Error(w, err)is the only place where
xerrors.Codevalues become HTTP status codes. All handler adapters route through it.
Custom error-to-status mappings elsewhere would fragment the contract.Validation is injected.
HandleandHandleEmptyaccept avalid.Validatorand
run validation before the business function is called. An invalid request never reaches
business logic.HandlerFuncfor manual handlers. When a handler needs direct access to
http.ResponseWriteror*http.Request(e.g. reading path parameters, setting custom
headers), useHandlerFunc. It still routes errors throughError.Known Limitations & Edge Cases
- Only JSON request body decoding is supported. Form data, multipart, and other content
types are not handled by the adapters. - No streaming response support. No server-sent events.
- No content-type negotiation. Responses are always
application/json(or empty for 204). HandleNoBodyperforms no query parameter or path variable validation. Read and validate
those values inside the function or useHandlerFunc.- Do not add context fields to
*xerrors.Errwith keys"code"or"message"— those
names are reserved in the JSON error body and will shadow the error code and message. httputilis a transport-layer package. Do not import it from business or service layers.
v0.9.0 → v1.0.0 Roadmap
- Validate error mapping completeness against all
xerrors.Codevalues added after v0.9.0. - Evaluate adding a
HandleStreamvariant for chunked/streaming JSON responses. - Consider adding a
Content-Typerequest check inHandleandHandleEmptyto return
415 Unsupported Media Type for non-JSON bodies. - Confirm behaviour with very large request bodies (no size limit is currently enforced).
Downloads