req is a lightweight HTTP client toolkit for Go (package name: req) built for production use: a simple request API, built-in retry and failover, fine-grained timeout and transport/TLS tuning, structured retry logging, and a dependency observer hook for telemetry — without pulling in a heavy framework.
- Why req?
- Features
- Install
- Quick Start
- Retry + Failover
- Dependency Observer
- Error Handling
- Configuration
- Comparison
- Examples
- Notes
- Status
- Contributing
- License
Go's standard net/http client is powerful but low-level: retries, failover, TLS tuning, and telemetry all have to be hand-rolled on every project. req wraps that boilerplate into a small, idiomatic API so you get:
- A minimal surface area — no framework to learn, just functional options
- Retry and failover behavior out of the box, without adding a separate library
- An observability hook ready to plug into AppInsights, Datadog, Prometheus, or any APM
- Full control when you need it (custom
*http.Client, custom transport, custom TLS config)
- ✅
Get,Post,Put,Delete,PostForm - ✅ Retry with configurable backoff and failover URLs
- ✅ Context-based timeout enforcement
- ✅ TLS and transport tuning (keep-alives, idle conns, custom
RoundTripper) - ✅ Structured retry logging
- ✅ Dependency observer hook for telemetry/APM integration
go get github.com/jeffotoni/reqpackage main
import (
"context"
"fmt"
"time"
req "github.com/jeffotoni/req"
)
func main() {
client := req.New(
req.WithHeaders(map[string]string{
"Authorization": "Bearer token",
}),
req.WithTimeout(5*time.Second),
)
resp, err := client.Get(context.Background(), "https://httpbin.org/get", nil)
if err != nil {
panic(err)
}
fmt.Println("status:", resp.StatusCode)
fmt.Println("body:", string(resp.Body))
}type payload struct {
Name string `json:"name"`
}
resp, err := client.Post(context.Background(), "https://httpbin.org/post", payload{Name: "jeffotoni"})
if err != nil {
panic(err)
}
fmt.Println("status:", resp.StatusCode)
fmt.Println("body:", string(resp.Body))client := req.New(
req.WithRetry(req.RetryConfig{
MaxRetries: 3,
Delay: 200 * time.Millisecond,
UseBackoff: true,
Statuses: []int{500, 502, 503, 504},
FailoverURLs: []string{"https://secondary.example.com"},
EnableLog: true,
}),
req.WithLogger(true),
)You can register a global dependency observer to receive outbound call events:
type myObserver struct{}
func (myObserver) ObserveDependency(ctx context.Context, ev req.DependencyEvent) {
// export to AppInsights / Datadog / Prometheus / etc.
}
req.SetDefaultDependencyObserver(myObserver{})
defer req.SetDefaultDependencyObserver(nil)The event includes fields like:
- dependency type / name / target / data
- start time + duration
- status code / result code
- success / error
- retry attempt / max retries
req returns standard Go error values from every request method. Wrap and inspect them with errors.Is / errors.As as needed:
resp, err := client.Get(ctx, url, nil)
if err != nil {
// TODO: document any sentinel errors or custom error types here,
// e.g. req.ErrTimeout, req.ErrMaxRetriesExceeded, etc.
panic(err)
}WithHeaders(map[string]string)WithRetry(RetryConfig)WithTimeout(time.Duration)WithLogger(bool)WithCustomHTTPClient(*http.Client)WithHTTPClientConfig(*HTTPClientConfig)WithProxyFromEnv()WithTLSConfig(*tls.Config)WithInsecureTLS(bool)WithDisableKeepAlives(bool)WithMaxIdleConns(int)WithMaxConnsPerHost(int)WithMaxIdleConnsPerHost(int)WithTransport(http.RoundTripper)WithTransportConfig(*http.Transport)
type RetryConfig struct {
MaxRetries int
Delay time.Duration
UseBackoff bool
Statuses []int
FailoverURLs []string
EnableLog bool
}| Feature | req |
net/http |
Other popular clients |
|---|---|---|---|
| Built-in retry + backoff | ✅ | ❌ | |
| Built-in failover URLs | ✅ | ❌ | ❌ |
| Dependency observer hook | ✅ | ❌ | |
| Structured retry logging | ✅ | ❌ | |
| Zero external dependencies | ✅ | ✅ |
See examples/:
examples/basic: basic GET/POST usageexamples/retry_failover: retry and failover flowexamples/crud: GET, POST, PUT and DELETE in one flowexamples/post_form: form POST withurl.Valuesexamples/dependency_observer: dependency event per attemptexamples/timeout_cancel: deadline and explicit cancelexamples/retry_backoff: retry with backoff, without failoverexamples/transport_tls: TLS and transport configurationexamples/custom_http_client: custom*http.Clientand transportexamples/proxy_env: proxy from environment variablesexamples/body_types: struct, string,[]byteandio.Readerbodiesexamples/header_override: default headers plus per-request override
Run:
go run ./examples/basic
go run ./examples/retry_failover- This project currently does not include benchmark reports in the repository.
- Retry behavior is status-driven via
RetryConfig.Statusesplus request errors (except canceled/deadline contexts). WithTimeoutenforces total operation timeout through context deadline.
req is under active development. The public API may still evolve before a v1.0 release — check the releases page for the latest changes.
Please read CONTRIBUTING.md.
This project is licensed under the terms described in LICENSE.