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/
This commit is contained in:
2026-03-19 13:04:37 +00:00
commit 6026ab8a5e
16 changed files with 860 additions and 0 deletions

57
README.md Normal file
View File

@@ -0,0 +1,57 @@
# httpclient
Resilient HTTP client with automatic retry and circuit breaking.
## Install
```
go get code.nochebuena.dev/go/httpclient
```
## Usage
```go
client := httpclient.NewWithDefaults(logger)
// or with custom config:
client = httpclient.New(logger, httpclient.Config{
Name: "payment-api",
MaxRetries: 3,
CBThreshold: 10,
})
resp, err := client.Do(req)
```
## Typed JSON helper
```go
order, err := httpclient.DoJSON[Order](ctx, client, req)
```
## Error mapping
```go
httpclient.MapStatusToError(404, "not found") // → xerrors.ErrNotFound
```
| HTTP status | xerrors code |
|---|---|
| 404 | `ErrNotFound` |
| 400 | `ErrInvalidInput` |
| 401 | `ErrUnauthorized` |
| 403 | `ErrPermissionDenied` |
| 409 | `ErrAlreadyExists` |
| 429 | `ErrUnavailable` |
| 5xx | `ErrInternal` |
## Configuration
| Env var | Default | Description |
|---|---|---|
| `HTTP_CLIENT_NAME` | `http` | Circuit breaker name |
| `HTTP_TIMEOUT` | `30s` | Overall request timeout |
| `HTTP_DIAL_TIMEOUT` | `5s` | TCP dial timeout |
| `HTTP_MAX_RETRIES` | `3` | Retry attempts |
| `HTTP_RETRY_DELAY` | `1s` | Base retry delay |
| `HTTP_CB_THRESHOLD` | `10` | Consecutive failures before open |
| `HTTP_CB_TIMEOUT` | `1m` | Time before half-open retry |