# 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.