Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

req

GoDoc Go Report License Go Version GitHub last commit GitHub contributors GitHub forks GitHub stars

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.

Table of Contents

Why req?

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)

Features

  • 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

Install

go get github.com/jeffotoni/req

Quick Start

GET request

package 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))
}

POST request with JSON 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))

Retry + Failover

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),
)

Dependency Observer

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

Error Handling

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)
}

Configuration

Client options

  • 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)

RetryConfig

type RetryConfig struct {
	MaxRetries   int
	Delay        time.Duration
	UseBackoff   bool
	Statuses     []int
	FailoverURLs []string
	EnableLog    bool
}

Comparison

Feature req net/http Other popular clients
Built-in retry + backoff ⚠️ varies
Built-in failover URLs
Dependency observer hook ⚠️ varies
Structured retry logging ⚠️ varies
Zero external dependencies ⚠️ varies

Examples

See examples/:

  • examples/basic: basic GET/POST usage
  • examples/retry_failover: retry and failover flow
  • examples/crud: GET, POST, PUT and DELETE in one flow
  • examples/post_form: form POST with url.Values
  • examples/dependency_observer: dependency event per attempt
  • examples/timeout_cancel: deadline and explicit cancel
  • examples/retry_backoff: retry with backoff, without failover
  • examples/transport_tls: TLS and transport configuration
  • examples/custom_http_client: custom *http.Client and transport
  • examples/proxy_env: proxy from environment variables
  • examples/body_types: struct, string, []byte and io.Reader bodies
  • examples/header_override: default headers plus per-request override

Run:

go run ./examples/basic
go run ./examples/retry_failover

Notes

  • This project currently does not include benchmark reports in the repository.
  • Retry behavior is status-driven via RetryConfig.Statuses plus request errors (except canceled/deadline contexts).
  • WithTimeout enforces total operation timeout through context deadline.

Status

req is under active development. The public API may still evolve before a v1.0 release — check the releases page for the latest changes.

Contributing

Please read CONTRIBUTING.md.

License

This project is licensed under the terms described in LICENSE.

About

A modern and resilient HTTP client for Go, built for high-performance distributed systems. It provides intelligent retries, automatic failover, exponential backoff, middleware support, observability, and seamless integration with cloud-native applications.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages