Files
httpclient/CHANGELOG.md
Rene Nochebuena 6026ab8a5e feat(httpclient): initial stable release v0.9.0
Resilient HTTP client with circuit breaking, exponential-backoff retry, X-Request-ID propagation, and a generic typed JSON helper.

What's included:
- Client interface with Do(req) method; New(logger, cfg) and NewWithDefaults(logger) constructors
- Config struct with env-tag support for timeout, dial timeout, retry, and circuit breaker parameters
- Retry via avast/retry-go/v4 with BackOffDelay; triggers only on network errors and HTTP 5xx
- Circuit breaker via sony/gobreaker wrapping the full retry loop; open circuit → xerrors.ErrUnavailable
- X-Request-ID header propagated automatically from context via logz.GetRequestID on every attempt
- DoJSON[T](ctx, client, req) generic helper for typed JSON request/response with xerrors error mapping
- MapStatusToError(code, msg) exported function mapping HTTP status codes to xerrors types

Tested-via: todo-api POC integration
Reviewed-against: docs/adr/
2026-03-19 13:04:37 +00:00

3.2 KiB

Changelog

All notable changes to this module will be documented in this file.

The format is based on Keep a Changelog, and this module adheres to Semantic Versioning.

0.9.0 - 2026-03-18

Added

  • Client interface: Do(req *http.Request) (*http.Response, error).
  • Config struct: fields Name, Timeout (default 30s), DialTimeout (default 5s), MaxRetries (default 3), RetryDelay (default 1s), CBThreshold (default 10), CBTimeout (default 1m); settable via HTTP_* environment variables.
  • DefaultConfig() Config: returns a Config populated with all default values.
  • New(logger logz.Logger, cfg Config) Client: constructs a client with a gobreaker circuit breaker wrapping an avast/retry-go retry loop. The circuit breaker trips after CBThreshold consecutive failures and resets after CBTimeout.
  • NewWithDefaults(logger logz.Logger) Client: convenience constructor; equivalent to New(logger, DefaultConfig()).
  • Retry behaviour: up to MaxRetries attempts with exponential backoff (retry.BackOffDelay). Only network errors and HTTP 5xx responses are retried; 4xx responses are not.
  • Circuit breaker behaviour: the breaker wraps the full retry sequence, so one fully-exhausted retry sequence counts as one failure. State transitions are logged at Warn level with name, from, and to fields.
  • Request ID propagation: logz.GetRequestID(ctx) is called on each attempt; if a request ID is present, it is forwarded as the X-Request-ID request header.
  • Per-request structured logging: successful requests emit an Info log with method, url, status, and latency fields; failed individual attempts emit a Debug log.
  • Error mapping on Do: open circuit → ErrUnavailable; post-retry HTTP error response → MapStatusToError; network/timeout error → ErrInternal.
  • DoJSON[T any](ctx context.Context, client Client, req *http.Request) (*T, error): generic free function that executes a request and decodes the JSON response body into T; returns *T on success and a xerrors-typed error for network failures, HTTP 4xx/5xx responses, unreadable bodies, or JSON decode failures.
  • MapStatusToError(code int, msg string) error (exported): maps HTTP status codes to xerrors codes — 404ErrNotFound, 400ErrInvalidInput, 401ErrUnauthorized, 403ErrPermissionDenied, 409ErrAlreadyExists, 429ErrUnavailable, all others → ErrInternal.
  • Dial timeout applied via a custom net.Dialer on the http.Transport, independent of the per-request Timeout.

Design Notes

  • The circuit breaker wraps the retry loop rather than individual attempts; a full set of exhausted retries registers as a single failure against the breaker threshold, preventing overly aggressive tripping.
  • DoJSON is a free generic function rather than a method so it works with any Client implementation, including mocks, without requiring a concrete type.
  • The module has no lifecycle (no OnInit/OnStart/OnStop) and does not depend on launcher or health; it is a stateless constructor suitable for use at any tier.