-
Release v0.9.0 Stable
released this
2026-03-19 07:05:28 -06:00 | 0 commits to main since this releasev0.9.0
code.nochebuena.dev/go/httpclientOverview
httpclientis a resilient HTTP client that wrapsnet/httpwith automatic retry, circuit
breaking, request-ID propagation, and a generic typed JSON helper. It is intended for
application services that make outbound calls to external HTTP APIs and need consistent
reliability behaviour and error mapping without boilerplate.This is the first stable release. The API was designed through multiple architecture reviews
and validated end-to-end via the todo-api proof-of-concept. It is versioned at v0.9.0 rather
than v1.0.0 because the library has not yet been exercised in production across all edge cases;
the pre-1.0 version preserves the option for minor API refinements without a major bump.What's Included
Clientinterface with a singleDo(req *http.Request) (*http.Response, error)methodNew(logger, cfg) Clientconstructor with fullConfigcontrolNewWithDefaults(logger) Clientconvenience constructorConfigstruct with env-tag support (HTTP_CLIENT_NAME,HTTP_TIMEOUT,
HTTP_DIAL_TIMEOUT,HTTP_MAX_RETRIES,HTTP_RETRY_DELAY,HTTP_CB_THRESHOLD,
HTTP_CB_TIMEOUT)- Retry via
github.com/avast/retry-go/v4with exponential backoff; retries only on
network errors and HTTP 5xx responses; 4xx responses are not retried - Circuit breaker via
github.com/sony/gobreaker; trips afterCBThresholdconsecutive
failures; open circuit returnsxerrors.ErrUnavailable - Circuit breaker wraps the entire retry loop: the breaker sees one failure per
fully-exhausted retry sequence, not per individual attempt X-Request-IDheader propagated from context to outbound requests vialogz.GetRequestID- Duck-typed
logz.Loggerinterface for log injection DoJSON[T](ctx, client, req) (*T, error)generic helper: executes the request, maps
HTTP 4xx/5xx to xerrors codes, and decodes the JSON body intoTMapStatusToError(code int, msg string) errorexported function mapping HTTP status codes
to xerrors types:- 404 →
ErrNotFound, 400 →ErrInvalidInput, 401 →ErrUnauthorized,
403 →ErrPermissionDenied, 409 →ErrAlreadyExists, 429 →ErrUnavailable,
all others →ErrInternal
- 404 →
Installation
go get code.nochebuena.dev/go/httpclient@v0.9.0Requires Go 1.21 or later. Depends on
code.nochebuena.dev/go/logz,
code.nochebuena.dev/go/xerrors,github.com/sony/gobreaker, and
github.com/avast/retry-go/v4.Does not depend on
launcherorhealth— it has no lifecycle and is not a component.Design Highlights
Circuit breaker wraps retry.
gobreaker.Executecontains the entireretry.Doloop.
The breaker counts one failure only when all retries are exhausted, not on each attempt. This
prevents transient 5xx bursts from immediately tripping the breaker.Retry only on 5xx. 4xx responses represent caller errors; retrying them is wasteful and
can have side effects. Only network errors and HTTP 5xx responses enter the retry loop.Request-ID propagation is automatic.
logz.GetRequestID(ctx)is called inside the retry
function on every attempt. No manual header setting is needed in calling code; the header is
omitted if no ID is present in the context.DoJSON[T]is a free function, not a method. It works with anyClientimplementation,
including mocks, without requiring a concrete type.MapStatusToErroris exported and independent. It can be used by any code that already
has an*http.Responsein hand, not just callers that went throughDoJSON.Known Limitations & Edge Cases
- No streaming support.
DoJSONreads the entire response body into memory.
client.Docan be used directly for streaming responses, but response body management
is left to the caller. - No multipart or form-encoded body helpers. Only JSON request/response is covered
by the helpers; other content types require manual construction. - Circuit breaker state is per-
Clientinstance. TwoClientvalues targeting the
same downstream service have independent circuit breakers. To share state across a
service's request handlers, inject a singleClientinstance. - Retry triggers only on 5xx. Responses in the 4xx range are not retried, even if the
caller believes the failure is transient (e.g. 429 Too Many Requests with a Retry-After
header is mapped toErrUnavailablebut not retried automatically).
v0.9.0 → v1.0.0 Roadmap
- Evaluate retry-on-429 with Retry-After header respect.
- Consider exposing circuit breaker state metrics (open/closed/half-open) for observability.
- Assess whether a streaming variant of
DoJSON(e.g.DoStream) is needed. - Consider a
DoJSONRequest[Req, Resp]helper that also serialises the request body. - Gather production feedback on default timeout and retry values before hardening the config.
Downloads