• v1.0.0 962b0ccf17

    Rene Nochebuena released this 2026-05-11 19:51:01 -06:00 | 1 commits to main since this release

    v1.0.0

    code.nochebuena.dev/go/httpclient

    Overview

    httpclient v1.0.0 commits the HTTP client API as stable. All v0.9.0 roadmap items are
    resolved. The module wraps net/http with automatic retry, circuit breaking, request-ID
    propagation, and generic typed JSON helpers for both outbound requests and responses.

    What Changed Since v0.9.0

    Retry-on-429 with Retry-After

    The retry loop now handles HTTP 429 Too Many Requests. Previously only network errors and
    >= 500 responses triggered a retry; 429 was returned immediately to the caller.

    When the server responds with 429 and includes a Retry-After header (integer seconds),
    that duration is used as the delay before the next attempt. Without the header, the
    configured BackOffDelay applies as usual.

    // Server responds: 429 Too Many Requests + Retry-After: 5
    // Client waits 5 seconds, then retries automatically.
    

    DoJSONRequest[Req, Resp any]

    New free generic function complementing DoJSON. Use it for POST/PUT/PATCH calls where
    the request also carries a JSON body:

    // Before (manual boilerplate every time):
    data, _ := json.Marshal(myReq)
    req, _ := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(data))
    req.Header.Set("Content-Type", "application/json")
    resp, err := httpclient.DoJSON[MyResponse](ctx, client, req)
    
    // After:
    resp, err := httpclient.DoJSONRequest[MyRequest, MyResponse](ctx, client, "POST", url, myReq)
    

    Use DoJSON for GET/DELETE (no request body); use DoJSONRequest for POST/PUT/PATCH.

    Dependency bumps

    logz and xerrors promoted to v1.0.0.

    Roadmap items resolved

    Item Resolution
    Retry-on-429 with Retry-After Done — respects header when present, falls back to BackOffDelay
    DoJSONRequest[Req, Resp] Done — serialises body, sets Content-Type, delegates to DoJSON
    Circuit breaker state metrics No — exposing gobreaker.State couples the Client interface to a third-party type; state transitions are already logged at Warn level
    DoStream No — streaming is a fundamentally different API contract; use client.Do directly for streaming responses
    Production feedback on defaults Validated as-is — 30s/5s/3 retries/1s delay/10 CB threshold/1m CB timeout confirmed in production

    Full API (stable)

    ClientDo(req *http.Request) (*http.Response, error).

    ConfigName, Timeout, DialTimeout, MaxRetries, RetryDelay, CBThreshold, CBTimeout; env-tag support.

    New(logger logz.Logger, cfg Config) Client — constructor with circuit breaker + retry.

    NewWithDefaults(logger logz.Logger) Client — convenience constructor with production defaults.

    DefaultConfig() Config — returns the default configuration values.

    DoJSON[T any](ctx, client, req) (*T, error) — executes a request and decodes the JSON response into T. Use for GET/DELETE.

    DoJSONRequest[Req, Resp any](ctx, client, method, url, body) (*Resp, error) — serialises body as JSON, constructs the request, and decodes the response. Use for POST/PUT/PATCH.

    MapStatusToError(code int, msg string) error — maps HTTP status codes to xerrors: 404ErrNotFound, 400ErrInvalidInput, 401ErrUnauthorized, 403ErrPermissionDenied, 409ErrAlreadyExists, 429ErrUnavailable, others → ErrInternal.

    Migration from v0.9.0

    No breaking changes. DoJSONRequest is a new addition; existing code using DoJSON is unaffected. The retry-on-429 behavior is a change in Do — callers that previously handled 429 manually after the call should review whether that handling is still needed.

    go get code.nochebuena.dev/go/httpclient@v1.0.0
    
    Downloads