httpreqx

package module
v0.0.3-beta.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2025 License: MIT Imports: 10 Imported by: 0

README

Go httpreqx

A thin wrapper around the standard net/http package that simplifies sending HTTP requests in Go.

Go Version

Features

  • Fluent API: Chain-based method calls for easy request building
  • Marshalers/Unmarshalers: Built-in JSON support with extensible interface
  • Request/Response Hooks: Middleware-like functionality for request/response processing
  • Error Handling: Optional request/response dumping and stack traces for debugging
  • Native Go Integration: Built on top of standard net/http package with zero external dependencies

Installation

go get github.com/AnotherFullstackDev/httpreqx

Quick Start

package main

import (
    "context"
    "fmt"
    "log"
    "github.com/AnotherFullstackDev/httpreqx"
)

func main() {
    ctx := context.Background()
    
    // Create a new HTTP client
    client := httpreqx.NewHttpClient()
    
    // Simple GET request
    resp, err := client.NewGetRequest(ctx, "https://httpbin.org/get").Do()
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    
    fmt.Printf("Status: %s\n", resp.Status)
}

Usage Examples

Basic HTTP Methods
ctx := context.Background()
client := httpreqx.NewHttpClient()

// GET request
resp, err := client.NewGetRequest(ctx, "https://api.example.com/users").Do()

// POST request with raw body
resp, err = client.NewPostRequest(ctx, "https://api.example.com/users", []byte(`{"name": "John"}`)).Do()

// PUT request
resp, err = client.NewPutRequest(ctx, "https://api.example.com/users/1", []byte(`{"name": "Jane"}`)).Do()

// DELETE request
resp, err = client.NewDeleteRequest(ctx, "https://api.example.com/users/1").Do()

// Other HTTP methods
resp, err = client.NewPatchRequest(ctx, "https://api.example.com/users/1", data).Do()
resp, err = client.NewOptionsRequest(ctx, "https://api.example.com/users").Do()
resp, err = client.NewHeadRequest(ctx, "https://api.example.com/users").Do()
JSON Marshaling/Unmarshaling
ctx := context.Background()

// Create client with JSON support
client := httpreqx.NewHttpClient().
    SetBodyMarshaler(httpreqx.NewJSONBodyMarshaler()).
    SetBodyUnmarshaler(httpreqx.NewJSONBodyUnmarshaler())

// POST with JSON body and JSON response
type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

user := User{Name: "John", Email: "john@example.com"}
var result map[string]interface{}

// WriteBodyTo automatically handles response body consumption and closing
// This is the recommended way to handle response bodies to prevent resource leaks
resp, err := client.NewPostRequest(ctx, "https://httpbin.org/post", user).
    WriteBodyTo(&result).
    Do()

if err != nil {
    log.Fatal(err)
}

fmt.Printf("Result: %+v\n", result)
Manual Response Body Handling
// If you don't use WriteBodyTo, you must manually close the response body
resp, err := client.NewGetRequest(ctx, "https://api.example.com/data").Do()
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close() // Important: prevent resource leaks!

// Read body manually
body, err := io.ReadAll(resp.Body)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Raw response: %s\n", string(body))
Custom Headers
client := httpreqx.NewHttpClient().
    SetHeader("Authorization", "Bearer token123").
    SetHeader("User-Agent", "MyApp/1.0")

// Or set multiple headers at once
headers := map[string]string{
    "Authorization": "Bearer token123",
    "User-Agent":    "MyApp/1.0",
    "Accept":        "application/json",
}
client = httpreqx.NewHttpClient().SetHeaders(headers)

// Per-request headers
resp, err := client.NewGetRequest(ctx, "https://api.example.com/data").
    SetHeader("X-Request-ID", "req-123").
    Do()
Request/Response Hooks
client := httpreqx.NewHttpClient().
    SetOnRequestReady(func(req *http.Request) error {
        // Log request details
        fmt.Printf("Making request to: %s\n", req.URL)
        // Add custom headers or modify request
        req.Header.Set("X-Timestamp", time.Now().Format(time.RFC3339))
        return nil
    }).
    SetOnResponseReady(func(resp *http.Response) error {
        // Log response details
        fmt.Printf("Response status: %s\n", resp.Status)
        return nil
    })

// Per-request hooks
resp, err := client.NewGetRequest(ctx, "https://api.example.com/data").
    SetOnRequestReady(func(req *http.Request) error {
        // Request-specific logic
        return nil
    }).
    SetOnResponseReady(func(resp *http.Response) error {
        // Response-specific logic
        return nil
    }).
    Do()
Error Handling and Debugging
// Enable request/response dumping on errors
// This automatically enables stack traces as well
client := httpreqx.NewHttpClient().SetDumpOnError()

// Enable only stack traces (without dumping)
client = httpreqx.NewHttpClient().SetStackTraceEnabled(true)

// SetDumpOnError logs:
// - HTTP request details (method, URL, headers, body)
// - HTTP response details (status, headers, body)
// - Original request body passed by caller
// - Stack trace of the error
resp, err := client.NewGetRequest(ctx, "https://httpbin.org/status/404").Do()
if err != nil {
    log.Printf("Error with detailed info: %v\n", err)
}

// Per-request debugging (overrides client-level settings)
resp, err = client.NewGetRequest(ctx, "https://httpbin.org/status/500").
    SetDumpOnError().  // Only for this request
    SetStackTraceEnabled(true).
    Do()
Client Cloning
// Create a base client
baseClient := httpreqx.NewHttpClient().
    SetBodyMarshaler(httpreqx.NewJSONBodyMarshaler()).
    SetBodyUnmarshaler(httpreqx.NewJSONBodyUnmarshaler()).
    SetHeader("User-Agent", "MyApp/1.0")

// Clone for specific use case
apiClient := baseClient.Clone().
    SetHeader("Authorization", "Bearer api-token")

// Clone for another use case
adminClient := baseClient.Clone().
    SetHeader("Authorization", "Bearer admin-token").
    SetTimeout(30 * time.Second)
Timeout Configuration
client := httpreqx.NewHttpClient().
    SetTimeout(10 * time.Second) // 10 second timeout

resp, err := client.NewGetRequest(ctx, "https://slow-api.example.com/data").Do()

Important Notes

Resource Management

Always use WriteBodyTo() when possible - it automatically handles response body consumption and closing to prevent resource leaks.

// ✅ Recommended: Automatic resource management
var result map[string]interface{}
resp, err := client.NewGetRequest(ctx, url).WriteBodyTo(&result).Do()

// ❌ Manual management: You must close the body yourself
resp, err := client.NewGetRequest(ctx, url).Do()
if err != nil {
    return err
}
defer resp.Body.Close() // Required to prevent resource leaks
Request vs Client Configuration

Most configuration methods can be set at both the client and request level:

  • Client-level: Affects all requests made by that client
  • Request-level: Overrides client settings for that specific request only
// Client-level configuration
client := httpreqx.NewHttpClient().
    SetHeader("User-Agent", "MyApp/1.0").
    SetBodyMarshaler(httpreqx.NewJSONBodyMarshaler())

// Request-level override (doesn't affect client)
resp, err := client.NewGetRequest(ctx, url).
    SetHeader("User-Agent", "SpecialRequest/1.0").  // Overrides client setting
    SetBodyMarshaler(httpreqx.NewNoopBodyMarshaler()). // Overrides client setting
    Do()

API Reference

HttpClient Methods
  • NewHttpClient() *HttpClient - Creates a new HTTP client with default settings
  • (*HttpClient) Clone() *HttpClient - Creates a copy of the client with the same configuration
  • (*HttpClient) SetTimeout(timeout time.Duration) *HttpClient - Sets the request timeout
  • (*HttpClient) SetHeader(key, value string) *HttpClient - Sets a single header
  • (*HttpClient) SetHeaders(headers map[string]string) *HttpClient - Sets multiple headers
  • (*HttpClient) SetBodyMarshaler(marshaler BodyMarshaler) *HttpClient - Sets the body marshaler
  • (*HttpClient) SetBodyUnmarshaler(unmarshaler BodyUnmarshaler) *HttpClient - Sets the body unmarshaler
  • (*HttpClient) SetOnRequestReady(hook OnRequestReadyHook) *HttpClient - Sets request hook
  • (*HttpClient) SetOnResponseReady(hook OnResponseReadyHook) *HttpClient - Sets response hook
  • (*HttpClient) SetDumpOnError() *HttpClient - Enables request/response dumping on errors
  • (*HttpClient) SetStackTraceEnabled(enabled bool) *HttpClient - Enables/disables stack traces
Request Creation Methods
  • (*HttpClient) NewRequest(ctx context.Context, method, path string, body interface{}) *Request - Creates a generic request (mostly used internally)
  • (*HttpClient) NewGetRequest(ctx context.Context, path string) *Request - Creates a GET request
  • (*HttpClient) NewPostRequest(ctx context.Context, path string, body interface{}) *Request - Creates a POST request
  • (*HttpClient) NewPutRequest(ctx context.Context, path string, body interface{}) *Request - Creates a PUT request
  • (*HttpClient) NewPatchRequest(ctx context.Context, path string, body interface{}) *Request - Creates a PATCH request
  • (*HttpClient) NewDeleteRequest(ctx context.Context, path string) *Request - Creates a DELETE request
  • (*HttpClient) NewOptionsRequest(ctx context.Context, path string) *Request - Creates an OPTIONS request
  • (*HttpClient) NewHeadRequest(ctx context.Context, path string) *Request - Creates a HEAD request
  • (*HttpClient) NewConnectRequest(ctx context.Context, path string) *Request - Creates a CONNECT request
  • (*HttpClient) NewTraceRequest(ctx context.Context, path string) *Request - Creates a TRACE request
Request Configuration Methods
  • (*Request) WriteBodyTo(result interface{}) *Request - Unmarshals response body to the provided variable. Automatically handles body consumption and closing to prevent resource leaks. This is the recommended way to handle response bodies.
  • (*Request) SetHeader(key, value string) *Request - Sets a header for this request (overrides client-level headers)
  • (*Request) SetHeaders(headers map[string]string) *Request - Sets multiple headers for this request (overrides client-level headers)
  • (*Request) SetBodyMarshaler(marshaler BodyMarshaler) *Request - Sets marshaler for this request (doesn't affect the client)
  • (*Request) SetBodyUnmarshaler(unmarshaler BodyUnmarshaler) *Request - Sets unmarshaler for this request (doesn't affect the client)
  • (*Request) SetOnRequestReady(hook OnRequestReadyHook) *Request - Sets request hook for this request (called after request is created and headers/body are set)
  • (*Request) SetOnResponseReady(hook OnResponseReadyHook) *Request - Sets response hook for this request (called after response is received, before processing)
  • (*Request) SetDumpOnError() *Request - Enables dumping for this request (logs request, response, and original body on errors; also enables stack traces)
  • (*Request) SetStackTraceEnabled(enabled bool) *Request - Enables/disables stack traces for this request
  • (*Request) Do() (*http.Response, error) - Executes the configured HTTP request and returns the response
Built-in Marshalers/Unmarshalers
  • NewJSONBodyMarshaler() BodyMarshaler - JSON marshaler that sets Content-Type: application/json
  • NewJSONBodyUnmarshaler() BodyUnmarshaler - JSON unmarshaler that sets Accept: application/json
  • NewNoopBodyMarshaler() BodyMarshaler - Pass-through marshaler for raw bytes
Utility Functions
  • IsSuccessResponse(resp *http.Response) bool - Checks if response status is 2xx
  • CloneRequestBody(req *http.Request) ([]byte, error) - Clones request body for inspection
HTTP Header Constants

The library provides constants for common HTTP headers:

// Authorization and Authentication
httpreqx.HeaderAuthorization    // "Authorization"
httpreqx.HeaderCookie          // "Cookie"
httpreqx.HeaderSetCookie       // "Set-Cookie"

// Content and Encoding
httpreqx.HeaderContentType     // "Content-Type"
httpreqx.HeaderContentLength   // "Content-Length"
httpreqx.HeaderAccept          // "Accept"
httpreqx.HeaderAcceptEncoding  // "Accept-Encoding"
httpreqx.HeaderAcceptLanguage  // "Accept-Language"

// Request Information
httpreqx.HeaderUserAgent       // "User-Agent"
httpreqx.HeaderHost            // "Host"
httpreqx.HeaderOrigin          // "Origin"
httpreqx.HeaderReferer         // "Referer"

// Caching and Conditional Requests
httpreqx.HeaderCacheControl    // "Cache-Control"
httpreqx.HeaderETag            // "ETag"
httpreqx.HeaderIfModifiedSince // "If-Modified-Since"
httpreqx.HeaderIfNoneMatch     // "If-None-Match"

// Security and Proxy
httpreqx.HeaderXRequestedWith     // "X-Requested-With"
httpreqx.HeaderXForwardedFor      // "X-Forwarded-For"
httpreqx.HeaderXFrameOptions      // "X-Frame-Options"
httpreqx.HeaderStrictTransportSec // "Strict-Transport-Security"

// Location and Redirection
httpreqx.HeaderLocation        // "Location"

Usage example:

client := httpreqx.NewHttpClient().
    SetHeader(httpreqx.HeaderAuthorization, "Bearer token123").
    SetHeader(httpreqx.HeaderUserAgent, "MyApp/1.0")

Compatibility

  • Minimum supported Go version: 1.20
  • Tested with: Go 1.20–1.23

License

This project is licensed under the MIT License.

Documentation

Index

Constants

View Source
const (
	HeaderAccept             = "Accept"
	HeaderAcceptEncoding     = "Accept-Encoding"
	HeaderAcceptLanguage     = "Accept-Language"
	HeaderAuthorization      = "Authorization"
	HeaderCacheControl       = "Cache-Control"
	HeaderContentLength      = "Content-Length"
	HeaderContentType        = "Content-Type"
	HeaderCookie             = "Cookie"
	HeaderHost               = "Host"
	HeaderOrigin             = "Origin"
	HeaderReferer            = "Referer"
	HeaderUserAgent          = "User-Agent"
	HeaderSetCookie          = "Set-Cookie"
	HeaderLocation           = "Location"
	HeaderETag               = "ETag"
	HeaderIfModifiedSince    = "If-Modified-Since"
	HeaderIfNoneMatch        = "If-None-Match"
	HeaderXRequestedWith     = "X-Requested-With"
	HeaderXForwardedFor      = "X-Forwarded-For"
	HeaderXFrameOptions      = "X-Frame-Options"
	HeaderStrictTransportSec = "Strict-Transport-Security"
)

Variables

This section is empty.

Functions

func CloneRequestBody

func CloneRequestBody(r *http.Request) ([]byte, error)

func IsSuccessResponse

func IsSuccessResponse(resp *http.Response) bool

Types

type BodyMarshaler

type BodyMarshaler interface {
	Marshal(body interface{}, writer io.Writer) error
	OnRequestReady(req *http.Request) error
}

func NewJSONBodyMarshaler

func NewJSONBodyMarshaler() BodyMarshaler

func NewNoopBodyMarshaler

func NewNoopBodyMarshaler() BodyMarshaler

type BodyUnmarshaler

type BodyUnmarshaler interface {
	Unmarshal(result interface{}, reader io.Reader) error
	OnRequestReady(req *http.Request) error
}

func NewJSONBodyUnmarshaler

func NewJSONBodyUnmarshaler() BodyUnmarshaler

type EnrichedError

type EnrichedError struct {
	Err   error
	Stack string
}

func (*EnrichedError) Error

func (e *EnrichedError) Error() string

type HttpClient

type HttpClient struct {
	// contains filtered or unexported fields
}

func NewHttpClient

func NewHttpClient() *HttpClient

func (*HttpClient) Clone

func (c *HttpClient) Clone() *HttpClient

func (*HttpClient) NewConnectRequest

func (c *HttpClient) NewConnectRequest(ctx context.Context, path string) *Request

func (*HttpClient) NewDeleteRequest

func (c *HttpClient) NewDeleteRequest(ctx context.Context, path string) *Request

func (*HttpClient) NewGetRequest

func (c *HttpClient) NewGetRequest(ctx context.Context, path string) *Request

func (*HttpClient) NewHeadRequest

func (c *HttpClient) NewHeadRequest(ctx context.Context, path string) *Request

func (*HttpClient) NewOptionsRequest

func (c *HttpClient) NewOptionsRequest(ctx context.Context, path string) *Request

func (*HttpClient) NewPatchRequest

func (c *HttpClient) NewPatchRequest(ctx context.Context, path string, body interface{}) *Request

func (*HttpClient) NewPostRequest

func (c *HttpClient) NewPostRequest(ctx context.Context, path string, body interface{}) *Request

func (*HttpClient) NewPutRequest

func (c *HttpClient) NewPutRequest(ctx context.Context, path string, body interface{}) *Request

func (*HttpClient) NewRequest

func (c *HttpClient) NewRequest(ctx context.Context, method, path string, body interface{}) *Request

NewRequest creates a new Request with the specified method, path, and body. It is mostly used internally. For convenience, you can use the NewGetRequest, NewPostRequest, etc. methods to create requests with common HTTP methods.

func (*HttpClient) NewTraceRequest

func (c *HttpClient) NewTraceRequest(ctx context.Context, path string) *Request

func (*HttpClient) SetBodyMarshaler

func (c *HttpClient) SetBodyMarshaler(marshaler BodyMarshaler) *HttpClient

func (*HttpClient) SetBodyUnmarshaler

func (c *HttpClient) SetBodyUnmarshaler(unmarshaler BodyUnmarshaler) *HttpClient

func (*HttpClient) SetDumpOnError

func (c *HttpClient) SetDumpOnError() *HttpClient

func (*HttpClient) SetHeader

func (c *HttpClient) SetHeader(key, value string) *HttpClient

func (*HttpClient) SetHeaders

func (c *HttpClient) SetHeaders(headers map[string]string) *HttpClient

func (*HttpClient) SetOnRequestReady

func (c *HttpClient) SetOnRequestReady(onRequestReady OnRequestReadyHook) *HttpClient

func (*HttpClient) SetOnResponseReady

func (c *HttpClient) SetOnResponseReady(onResponseReady OnResponseReadyHook) *HttpClient

func (*HttpClient) SetStackTraceEnabled

func (c *HttpClient) SetStackTraceEnabled(enabled bool) *HttpClient

func (*HttpClient) SetTimeout

func (c *HttpClient) SetTimeout(timeout time.Duration) *HttpClient

type JSONBodyMarshaler

type JSONBodyMarshaler struct{}

func (*JSONBodyMarshaler) Marshal

func (m *JSONBodyMarshaler) Marshal(body interface{}, writer io.Writer) error

func (*JSONBodyMarshaler) OnRequestReady

func (m *JSONBodyMarshaler) OnRequestReady(req *http.Request) error

type JSONBodyUnmarshaler

type JSONBodyUnmarshaler struct{}

func (*JSONBodyUnmarshaler) OnRequestReady

func (u *JSONBodyUnmarshaler) OnRequestReady(req *http.Request) error

func (*JSONBodyUnmarshaler) Unmarshal

func (u *JSONBodyUnmarshaler) Unmarshal(result interface{}, reader io.Reader) error

type NoopBodyMarshaler

type NoopBodyMarshaler struct{}

func (*NoopBodyMarshaler) Marshal

func (m *NoopBodyMarshaler) Marshal(body interface{}, writer io.Writer) error

func (*NoopBodyMarshaler) OnRequestReady

func (m *NoopBodyMarshaler) OnRequestReady(req *http.Request) error

type OnRequestReadyHook

type OnRequestReadyHook func(req *http.Request) error

type OnResponseReadyHook

type OnResponseReadyHook func(resp *http.Response) error

type Request

type Request struct {
	// contains filtered or unexported fields
}

func (*Request) Do

func (r *Request) Do() (*http.Response, error)

Do method executes the configured HTTP request and returns the http.Response.

func (*Request) SetBodyMarshaler

func (r *Request) SetBodyMarshaler(marshaler BodyMarshaler) *Request

SetBodyMarshaler sets the BodyMarshaler at the request level. Does not affect the client.

func (*Request) SetBodyUnmarshaler

func (r *Request) SetBodyUnmarshaler(unmarshaler BodyUnmarshaler) *Request

SetBodyUnmarshaler sets the BodyUnmarshaler at the request level. Does not affect the client.

func (*Request) SetDumpOnError

func (r *Request) SetDumpOnError() *Request

SetDumpOnError configures logging of the request, response and error when an error occurs. http.Request and http.Response bodies will be logged as well, if they are set. original, body passed by the caller code will be logged as well, if it is set. This method will also enable the StackTraceEnabled option, which will add a stack trace to the error if it occurs.

func (*Request) SetHeader

func (r *Request) SetHeader(key, value string) *Request

SetHeader sets a single header for the request. This will override any headers set at the client level but only for this request.

func (*Request) SetHeaders

func (r *Request) SetHeaders(headers map[string]string) *Request

SetHeaders sets the headers for the request. This will override any headers set at the client level but only for this request.

func (*Request) SetOnRequestReady

func (r *Request) SetOnRequestReady(onRequestReady OnRequestReadyHook) *Request

SetOnRequestReady sets a hook that will be called right after an http.Request is created and all headers and body are set. This method will override any hooks set at the client level, without affecting the client, but only for this request.

func (*Request) SetOnResponseReady

func (r *Request) SetOnResponseReady(onResponseReady OnResponseReadyHook) *Request

SetOnResponseReady sets a hook that will be called right after the response is received and before it is processed. This method will override any hooks set at the client level, without affecting the client, but only for this request.

func (*Request) SetStackTraceEnabled

func (r *Request) SetStackTraceEnabled(enabled bool) *Request

SetStackTraceEnabled enables or disables the stack trace in the error if it occurs.

func (*Request) WriteBodyTo

func (r *Request) WriteBodyTo(result interface{}) *Request

WriteBodyTo sets the destination for unmarshalling the response body. This method will consume the response body and close it after reading. This is the recommended way to consume the response body as it prevents resource leaks, provides type safety and a unified way to work with body. In case this method in not used, the caller must close the response body manually after reading it to prevent resource leaks!

type RequestOptions

type RequestOptions struct {
	BodyMarshaler     BodyMarshaler
	BodyUnmarshaler   BodyUnmarshaler
	Headers           map[string]string
	OnRequestReady    OnRequestReadyHook
	OnResponseReady   OnResponseReadyHook
	OnErrorHooks      []onErrorHook
	StackTraceEnabled bool
}

func (*RequestOptions) Clone

func (o *RequestOptions) Clone() *RequestOptions

func (*RequestOptions) SetBodyMarshaler

func (o *RequestOptions) SetBodyMarshaler(marshaler BodyMarshaler)

func (*RequestOptions) SetBodyUnmarshaler

func (o *RequestOptions) SetBodyUnmarshaler(unmarshaler BodyUnmarshaler)

func (*RequestOptions) SetDumpOnError

func (o *RequestOptions) SetDumpOnError()

func (*RequestOptions) SetHeader

func (o *RequestOptions) SetHeader(key, value string)

func (*RequestOptions) SetHeaders

func (o *RequestOptions) SetHeaders(headers map[string]string)

func (*RequestOptions) SetOnRequestReady

func (o *RequestOptions) SetOnRequestReady(onRequestReady OnRequestReadyHook)

func (*RequestOptions) SetOnResponseReady

func (o *RequestOptions) SetOnResponseReady(onResponseReady OnResponseReadyHook)

func (*RequestOptions) SetStackTraceEnabled

func (o *RequestOptions) SetStackTraceEnabled(enabled bool)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL