client

package
v0.0.9 Latest Latest
Warning

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

Go to latest
Published: Dec 16, 2025 License: MIT Imports: 10 Imported by: 0

README

Universal HTTP Client for Go

A lightweight, flexible HTTP client with sensible defaults and powerful customization options.

Features

  • Simple API: Clean, intuitive methods for common HTTP operations
  • Type-Safe Responses: Generic functions for decoding responses to any struct type
  • Flexible Configuration: Extensive options for timeouts, retries, and connections
  • Authentication Support: Built-in methods for Basic Auth, Bearer tokens, and custom authentication
  • Automatic Retries: Configurable retry policies with exponential backoff
  • Environment-Aware: Reads configuration from environment variables when available
  • Fluent Interface: Chainable configuration methods

Installation

go get github.com/medatechnology/goutil/httpclient

Quick Start

Basic Usage
package main

import (
    "fmt"
    "github.com/medatechnology/goutil/httpclient"
)

func main() {
    // Create a new client with default configuration
    client := httpclient.NewClient()
    
    // Make a GET request and decode to a map
    response, err := client.Get("https://api.example.com/users")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    fmt.Printf("Response: %v\n", response)
}
Configuring the Client

It is best practice to just use one (1) instance of client. Because inside basically it's just http.Client and http package have channels for Client, which can make many multiple API calls from single instance. If you create multiple simplehttp.Client the overhead will be high and less efficient. If you are planning to call large number of http calls, then better to setup pools of simplehttp.Client, but even 10 instances already can handle near 100K calls.

// Create a client with custom configuration
client := httpclient.NewClient(
    httpclient.WithBaseURL("https://api.example.com"),
    httpclient.WithTimeout(30 * time.Second),
    httpclient.WithMaxRetries(5),
    httpclient.WithBearerToken("your-token-here"),
    httpclient.WithHeader("X-API-Key", "your-api-key"),
)
POST Request with JSON Body
// Create a POST request with a JSON body
data := map[string]interface{}{
    "name": "John Doe",
    "email": "john@example.com",
}

response, err := client.Post("/users", data)
if err != nil {
    fmt.Printf("Error: %v\n", err)
    return
}

fmt.Printf("Created user with ID: %v\n", response["id"])
Decoding to a Struct
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// Get a user and decode directly to struct
user, err := httpclient.GetAs[User](client, "/users/42")
if err != nil {
    fmt.Printf("Error: %v\n", err)
    return
}

fmt.Printf("User: %s (%s)\n", user.Name, user.Email)
Handling Form Data
// Send form data
formData := map[string]string{
    "username": "johndoe",
    "password": "secret",
}

response, err := client.Post("/login", formData, httpclient.WithFormContentType())
if err != nil {
    fmt.Printf("Error: %v\n", err)
    return
}

fmt.Printf("Auth token: %v\n", response["token"])

Advanced Usage

Custom Retry Policy
// Create a custom retry policy
customRetryPolicy := func(resp *http.Response, err error) bool {
    // Retry on network errors
    if err != nil {
        return true
    }
    
    // Retry on 429 Too Many Requests
    if resp != nil && resp.StatusCode == 429 {
        return true
    }
    
    // Retry on 5xx server errors
    if resp != nil && resp.StatusCode >= 500 {
        return true
    }
    
    return false
}

// Use the custom retry policy
client := httpclient.NewClient(
    httpclient.WithRetryPolicy(customRetryPolicy),
    httpclient.WithMaxRetries(3),
    httpclient.WithRetryDelay(2 * time.Second),
)
Handling Raw Responses
// Get raw bytes
bytes, err := client.GetBytes("/files/document.pdf")
if err != nil {
    fmt.Printf("Error: %v\n", err)
    return
}

// Save to file
ioutil.WriteFile("document.pdf", bytes, 0644)
Batch Requests
// Define request functions
requests := []func() (interface{}, error){
    func() (interface{}, error) {
        return httpclient.GetAs[User](client, "/users/1")
    },
    func() (interface{}, error) {
        return httpclient.GetAs[User](client, "/users/2")
    },
    func() (interface{}, error) {
        return httpclient.GetAs[User](client, "/users/3")
    },
}

// Execute requests concurrently
results := make([]interface{}, len(requests))
errors := make([]error, len(requests))
var wg sync.WaitGroup

for i, request := range requests {
    wg.Add(1)
    go func(i int, req func() (interface{}, error)) {
        defer wg.Add(-1)
        results[i], errors[i] = req()
    }(i, request)
}

wg.Wait()

// Process results
for i, result := range results {
    if errors[i] != nil {
        fmt.Printf("Request %d failed: %v\n", i, errors[i])
        continue
    }
    
    user := result.(User)
    fmt.Printf("User %d: %s\n", i, user.Name)
}
Custom Authentication
// Create client with API key authentication
client := httpclient.NewClient(
    httpclient.WithHeader("X-API-Key", "your-api-key"),
)

// Make authenticated request
response, err := client.Get("/protected-resource")

Configuration Options

Client Options
Option Description Default
WithBaseURL Sets the base URL for all requests ""
WithTimeout Sets the overall request timeout 60s
WithHeader Adds a single header to all requests -
WithHeaders Sets multiple headers at once -
WithQueryParam Adds a query parameter to all requests -
WithQueryParams Sets multiple query parameters -
WithBasicAuth Sets basic authentication credentials -
WithBearerToken Sets bearer token authentication -
WithContentType Sets the default content type application/json
WithMaxRetries Sets the number of retry attempts 3
WithRetryDelay Sets the delay between retries 1s
WithRetryPolicy Sets a custom retry policy DefaultRetryPolicy
Connection Options
Option Description Default
WithDialTimeout Sets the connection dial timeout 30s
WithKeepAlive Sets the keep-alive duration 30s
WithTLSHandshakeTimeout Sets the TLS handshake timeout 10s
WithResponseHeaderTimeout Sets the response header timeout 60s
WithExpectContinueTimeout Sets the expect-continue timeout 5s
WithIdleConnectionTimeout Sets the idle connection timeout 90s
WithMaxIdleConnections Sets the maximum idle connections 100
WithMaxIdleConnectionsPerHost Sets the maximum idle connections per host 100
WithMaxConnectionsPerHost Sets the maximum connections per host 1000

Environment Variables

The client can be configured using the following environment variables:

Variable Description Default
HTTP_TIMEOUT Overall request timeout 60s
HTTP_DIAL_TIMEOUT Connection dial timeout 30s
HTTP_KEEP_ALIVE Keep-alive duration 30s
HTTP_TLS_TIMEOUT TLS handshake timeout 10s
HTTP_RESPONSE_TIMEOUT Response header timeout 60s
HTTP_CONTINUE_TIMEOUT Expect-continue timeout 5s
HTTP_IDLE_CONN_TIMEOUT Idle connection timeout 90s
HTTP_MAX_IDLE_CONNS Maximum idle connections 100
HTTP_MAX_IDLE_CONNS_PER_HOST Maximum idle connections per host 100
HTTP_MAX_CONNS_PER_HOST Maximum connections per host 1000
HTTP_MAX_RETRIES Maximum retry attempts 3
HTTP_RETRY_DELAY Delay between retries 1s
HTTP_BASE_URL Base URL for all requests ""

Example Response Handling

Map Response
response, err := client.Get("/users")
if err != nil {
    fmt.Printf("Error: %v\n", err)
    return
}

// Example response:
// {
//   "users": [
//     {"id": 1, "name": "John"},
//     {"id": 2, "name": "Jane"}
//   ],
//   "total": 2
// }

users := response["users"].([]interface{})
for _, user := range users {
    userData := user.(map[string]interface{})
    fmt.Printf("User %v: %s\n", userData["id"], userData["name"])
}
Struct Response
type UserResponse struct {
    Users []User `json:"users"`
    Total int    `json:"total"`
}

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

response, err := httpclient.GetAs[UserResponse](client, "/users")
if err != nil {
    fmt.Printf("Error: %v\n", err)
    return
}

// Example response structure:
// UserResponse{
//   Users: []User{
//     {ID: 1, Name: "John"},
//     {ID: 2, Name: "Jane"},
//   },
//   Total: 2,
// }

for _, user := range response.Users {
    fmt.Printf("User %d: %s\n", user.ID, user.Name)
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

View Source
const (
	// Default HTTP timeouts
	DEFAULT_TIMEOUT                       = 60 * time.Second
	DEFAULT_DIAL_TIMEOUT                  = 30 * time.Second
	DEFAULT_KEEP_ALIVE                    = 30 * time.Second
	DEFAULT_TLS_HANDSHAKE_TIMEOUT         = 10 * time.Second
	DEFAULT_RESPONSE_HEADER_TIMEOUT       = 60 * time.Second
	DEFAULT_EXPECT_CONTINUE_TIMEOUT       = 5 * time.Second
	DEFAULT_MAX_IDLE_CONNECTIONS          = 100
	DEFAULT_MAX_IDLE_CONNECTIONS_PER_HOST = 100
	DEFAULT_MAX_CONNECTIONS_PER_HOST      = 1000
	DEFAULT_IDLE_CONNECTION_TIMEOUT       = 90 * time.Second
	DEFAULT_MAX_RETRIES                   = 3
	DEFAULT_RETRY_DELAY                   = 1 * time.Second

	// Content type constants
	CONTENT_TYPE_JSON         = "application/json"
	CONTENT_TYPE_FORM         = "application/x-www-form-urlencoded"
	CONTENT_TYPE_MULTIPART    = "multipart/form-data"
	CONTENT_TYPE_TEXT         = "text/plain"
	CONTENT_TYPE_XML          = "application/xml"
	CONTENT_TYPE_OCTET_STREAM = "application/octet-stream"
)

HTTP client configuration constants

Variables

This section is empty.

Functions

func DecodeResponse

func DecodeResponse[T any](resp *http.Response) (T, error)

DecodeResponse is a generic function to decode an HTTP response into the specified type

func DefaultRetryPolicy

func DefaultRetryPolicy(resp *http.Response, err error) bool

DefaultRetryPolicy provides a reasonable default retry policy

func DeleteAs

func DeleteAs[T any](c *Client, endpoint string, options ...ClientOption) (T, error)

DeleteAs performs an HTTP DELETE request and decodes the response to the specified type

func GetAs

func GetAs[T any](c *Client, endpoint string, options ...ClientOption) (T, error)

GetAs performs an HTTP GET request and decodes the response to the specified type

func NewHTTPClient

func NewHTTPClient(config *ClientConfig, options ...ClientOption) *http.Client

NewHTTPClient creates and configures a new HTTP client

func PatchAs

func PatchAs[T any](c *Client, endpoint string, body interface{}, options ...ClientOption) (T, error)

PatchAs performs an HTTP PATCH request and decodes the response to the specified type

func PostAs

func PostAs[T any](c *Client, endpoint string, body interface{}, options ...ClientOption) (T, error)

PostAs performs an HTTP POST request and decodes the response to the specified type

func PutAs

func PutAs[T any](c *Client, endpoint string, body interface{}, options ...ClientOption) (T, error)

PutAs performs an HTTP PUT request and decodes the response to the specified type

func RequestAs

func RequestAs[T any](c *Client, method, endpoint string, body interface{}, options ...ClientOption) (T, error)

RequestAs performs an HTTP request and decodes the response to the specified type

Types

type Client

type Client struct {
	Config     ClientConfig
	HTTPClient *http.Client
}

Client is the main HTTP client interface

func NewClient

func NewClient(options ...ClientOption) *Client

NewClient creates a new HTTP client with the provided configuration Actually this should be better to have it's own simpleClientOptions instead of ClientOption which belongs to http.Client options. Then one of them is just WithConfig(NewDefaultConfig(ClientOption))

func (*Client) Delete

func (c *Client) Delete(endpoint string, options ...ClientOption) (map[string]interface{}, error)

Delete performs an HTTP DELETE request and returns the result as a JSON map

func (*Client) Get

func (c *Client) Get(endpoint string, options ...ClientOption) (map[string]interface{}, error)

Get performs an HTTP GET request and returns the result as a JSON map

func (*Client) GetBytes

func (c *Client) GetBytes(endpoint string, options ...ClientOption) ([]byte, error)

GetBytes gets the raw bytes from an HTTP response

func (*Client) GetString

func (c *Client) GetString(endpoint string, options ...ClientOption) (string, error)

GetString gets the response as a string

func (*Client) Patch

func (c *Client) Patch(endpoint string, body interface{}, options ...ClientOption) (map[string]interface{}, error)

Patch performs an HTTP PATCH request and returns the result as a JSON map

func (*Client) Post

func (c *Client) Post(endpoint string, body interface{}, options ...ClientOption) (map[string]interface{}, error)

Post performs an HTTP POST request and returns the result as a JSON map

func (*Client) Put

func (c *Client) Put(endpoint string, body interface{}, options ...ClientOption) (map[string]interface{}, error)

Put performs an HTTP PUT request and returns the result as a JSON map

func (*Client) Request

func (c *Client) Request(method, endpoint string, body interface{}, options ...ClientOption) (*http.Response, error)

Request performs an HTTP request and returns the raw response

func (*Client) SetBasicAuth

func (c *Client) SetBasicAuth(username, password string) *Client

SetBasicAuth sets basic authentication credentials

func (*Client) SetBearerToken

func (c *Client) SetBearerToken(token string) *Client

SetBearerToken sets a bearer token for authentication

func (*Client) SetContentType

func (c *Client) SetContentType(contentType string) *Client

SetContentType sets the content type for requests

func (*Client) SetHeader

func (c *Client) SetHeader(key string, values ...string) *Client

SetHeader sets or adds a header to the client's default headers

func (*Client) SetQueryParam

func (c *Client) SetQueryParam(key, value string) *Client

SetQueryParam sets a query parameter to the client's default parameters

type ClientConfig

type ClientConfig struct {
	// Basic settings
	BaseURL     string
	Headers     map[string][]string
	QueryParams map[string]string
	ContentType string

	// Authentication
	Username  string
	Password  string
	Token     string
	TokenType string

	// Error handling
	ErrorResult interface{}

	// Timeout settings
	Timeout               time.Duration
	DialTimeout           time.Duration
	KeepAlive             time.Duration
	TLSHandshakeTimeout   time.Duration
	ResponseHeaderTimeout time.Duration
	ExpectContinueTimeout time.Duration
	IdleConnectionTimeout time.Duration

	// Connection settings
	MaxIdleConnections  int
	MaxIdleConnsPerHost int
	MaxConnsPerHost     int

	// Retry settings
	MaxRetries  int
	RetryDelay  time.Duration
	RetryPolicy RetryPolicy
}

ClientConfig holds configuration options for the HTTP client

func NewDefaultConfig

func NewDefaultConfig(options ...ClientOption) *ClientConfig

NewDefaultConfig creates a new configuration with default values

type ClientOption

type ClientOption func(*ClientConfig)

ClientOption defines a function that modifies client configuration

func NoRetry

func NoRetry() ClientOption

NoRetry disables retries

func WithBaseURL

func WithBaseURL(url string) ClientOption

WithBaseURL sets the base URL for the client

func WithBasicAuth

func WithBasicAuth(username, password string) ClientOption

WithBasicAuth sets basic authentication for requests

func WithBearerToken

func WithBearerToken(token string) ClientOption

WithBearerToken sets bearer token authentication for requests

func WithContentType

func WithContentType(contentType string) ClientOption

WithContentType sets the content type for requests

func WithCustomToken

func WithCustomToken(tokenType, token string) ClientOption

WithCustomToken sets a custom token authentication for requests

func WithDialTimeout

func WithDialTimeout(timeout time.Duration) ClientOption

WithDialTimeout sets the connection dial timeout

func WithErrorResult

func WithErrorResult(result interface{}) ClientOption

WithErrorResult sets a result object for error responses

func WithExpectContinueTimeout

func WithExpectContinueTimeout(timeout time.Duration) ClientOption

WithExpectContinueTimeout sets the expect continue timeout

func WithFormContentType

func WithFormContentType() ClientOption

WithFormContentType sets the content type to application/x-www-form-urlencoded

func WithHeader

func WithHeader(key string, values ...string) ClientOption

WithHeader adds a single header

func WithHeaders

func WithHeaders(headers map[string][]string) ClientOption

WithHeaders sets headers for requests

func WithIdleConnectionTimeout

func WithIdleConnectionTimeout(timeout time.Duration) ClientOption

WithIdleConnectionTimeout sets the idle connection timeout

func WithJSONContentType

func WithJSONContentType() ClientOption

WithJSONContentType sets the content type to application/json

func WithKeepAlive

func WithKeepAlive(keepAlive time.Duration) ClientOption

WithKeepAlive sets the keep-alive duration

func WithMaxConnectionsPerHost

func WithMaxConnectionsPerHost(max int) ClientOption

WithMaxConnectionsPerHost sets the maximum number of connections per host

func WithMaxIdleConnections

func WithMaxIdleConnections(max int) ClientOption

WithMaxIdleConnections sets the maximum number of idle connections

func WithMaxIdleConnectionsPerHost

func WithMaxIdleConnectionsPerHost(max int) ClientOption

WithMaxIdleConnectionsPerHost sets the maximum number of idle connections per host

func WithMaxRetries

func WithMaxRetries(max int) ClientOption

WithMaxRetries sets the maximum number of retry attempts

func WithMultipartContentType

func WithMultipartContentType() ClientOption

WithMultipartContentType sets the content type to multipart/form-data

func WithQueryParam

func WithQueryParam(key, value string) ClientOption

WithQueryParam adds a single query parameter

func WithQueryParams

func WithQueryParams(params map[string]string) ClientOption

WithQueryParams sets query parameters for requests

func WithResponseHeaderTimeout

func WithResponseHeaderTimeout(timeout time.Duration) ClientOption

WithResponseHeaderTimeout sets the response header timeout

func WithRetryDelay

func WithRetryDelay(delay time.Duration) ClientOption

WithRetryDelay sets the delay between retry attempts

func WithRetryPolicy

func WithRetryPolicy(policy RetryPolicy) ClientOption

WithRetryPolicy sets a custom retry policy

func WithTLSHandshakeTimeout

func WithTLSHandshakeTimeout(timeout time.Duration) ClientOption

WithTLSHandshakeTimeout sets the TLS handshake timeout

func WithTimeout

func WithTimeout(timeout time.Duration) ClientOption

WithTimeout sets the overall request timeout

type RetryPolicy

type RetryPolicy func(resp *http.Response, err error) bool

RetryPolicy determines if a request should be retried

type StatusCode

type StatusCode int

StatusCode represents an HTTP status code

Jump to

Keyboard shortcuts

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