diff --git a/README.md b/README.md index c8b65ab..733bd67 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,95 @@ # httpx +HTTP client for Go microservices. Retry, load balancing, circuit breaking, all as `http.RoundTripper` middleware. stdlib only, zero external deps. + +``` +go get git.codelab.vc/pkg/httpx +``` + +## Quick start + +```go +client := httpx.New( + httpx.WithBaseURL("https://api.example.com"), + httpx.WithTimeout(10*time.Second), + httpx.WithRetry(retry.WithMaxAttempts(3)), + httpx.WithMiddleware( + middleware.UserAgent("my-service/1.0"), + middleware.BearerAuth(func(ctx context.Context) (string, error) { + return os.Getenv("API_TOKEN"), nil + }), + ), +) +defer client.Close() + +resp, err := client.Get(ctx, "/users/123") +if err != nil { + log.Fatal(err) +} + +var user User +resp.JSON(&user) +``` + +## Packages + +Everything is a `func(http.RoundTripper) http.RoundTripper`. Use them with `httpx.Client` or plug into a plain `http.Client`. + +| Package | What it does | +|---------|-------------| +| `retry` | Exponential/constant backoff, Retry-After support. Idempotent methods only by default. | +| `balancer` | Round robin, failover, weighted random. Optional background health checks. | +| `circuitbreaker` | Per-host state machine (closed/open/half-open). Stops hammering dead endpoints. | +| `middleware` | Logging (slog), default headers, bearer/basic auth, panic recovery. | + +The client assembles them in this order: + +``` +Request → Logging → Your Middleware → Retry → Circuit Breaker → Balancer → Transport +``` + +Retry wraps the circuit breaker and balancer, so each attempt can pick a different endpoint. + +## Multi-DC setup + +```go +client := httpx.New( + httpx.WithEndpoints( + balancer.Endpoint{URL: "https://dc1.api.internal", Weight: 3}, + balancer.Endpoint{URL: "https://dc2.api.internal", Weight: 1}, + ), + httpx.WithBalancer(balancer.WithStrategy(balancer.WeightedRandom())), + httpx.WithRetry(retry.WithMaxAttempts(4)), + httpx.WithCircuitBreaker(circuitbreaker.WithFailureThreshold(5)), + httpx.WithLogger(slog.Default()), +) +defer client.Close() +``` + +## Standalone usage + +Each component works with any `http.Client`, no need for the full wrapper: + +```go +// Just retry, nothing else +transport := retry.Transport(retry.WithMaxAttempts(3)) +httpClient := &http.Client{ + Transport: transport(http.DefaultTransport), +} +``` + +```go +// Chain a few middlewares together +chain := middleware.Chain( + middleware.Logging(slog.Default()), + middleware.UserAgent("my-service/1.0"), + retry.Transport(retry.WithMaxAttempts(2)), +) +httpClient := &http.Client{ + Transport: chain(http.DefaultTransport), +} +``` + +## Requirements + +Go 1.24+, stdlib only.