fetch

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: MIT Imports: 12 Imported by: 0

README ΒΆ

🌐 fetch-go

An ergonomic, axios-inspired HTTP client for Go

Go Version Go Reference Go Report Card Tests


fetch is a lightweight, zero-dependency HTTP client for Go that brings the ergonomics of JavaScript's axios to idiomatic Go. It wraps net/http with a fluent builder API, typed errors, interceptors, and first-class JSON support β€” without hiding the standard library from you.

err := fetch.Get("https://jsonplaceholder.typicode.com/posts").Scan(&posts)
if err != nil {
    log.Fatal("Fetch error:", err)
}

log.Printf("First post: %+v", posts)


// -------------> With Client <------------- //

client := fetch.New("https://api.example.com")

var users []User
err := client.Get("/users").
    WithParam("page", "1").
    WithBearerToken(token).
    Scan(&users)

// POST with JSON body
resp, err := client.Post("/users", User{Name: "Alice"}).Do()

// PUT
resp, err := client.Put("/users/1", User{Name: "Alice Updated"}).Do()

// PATCH
resp, err := client.Patch("/users/1", map[string]string{"name": "Alice"}).Do()

// DELETE (body is optional β€” pass nil)
resp, err := client.Delete("/users/1", nil).Do()

Table of Contents


Features

  • Fluent builder API β€” chain methods naturally, no boilerplate
  • All HTTP verbs β€” GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
  • Body formats β€” JSON, form-encoded, multipart, plain text, raw io.Reader
  • Typed errors β€” non-2xx responses become *FetchError with status helpers
  • Interceptors β€” hook into every request and response (logging, auth, retries)
  • First-class JSON β€” Scan(&v) encodes the body and decodes the response in one call
  • Auth helpers β€” WithBearerToken, WithBasicAuth
  • Zero external dependencies β€” only the Go standard library
  • Context-aware β€” every request respects context.Context for cancellation and deadlines
  • Safe for concurrent use β€” share one Client across all goroutines

Requirements

Requirement Version
Go 1.22 or higher

No external dependencies. go.sum will be empty.


Installation

go get github.com/rixotech/fetch-go@latest

Then import it in your code:

import "github.com/rixotech/fetch-go"

Quick Start

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/rixotech/fetch-go"
)

type Post struct {
    ID    int    `json:"id"`
    Title string `json:"title"`
    Body  string `json:"body"`
}

func main() {
    // Create a reusable client
    client, err := fetch.New("https://jsonplaceholder.typicode.com",
        fetch.WithTimeout(10*time.Second),
    )
    if err != nil {
        log.Fatal(err)
    }

    ctx := context.Background()

    // GET β€” decode response into a struct
    var post Post
    if err := client.GetWithContext(ctx, "/posts/1").Scan(&post); err != nil {
        log.Fatal(err)
    }
    fmt.Println(post.Title)

    // POST β€” send JSON body, decode created resource
    var created Post
    err = client.PostWithContext(ctx, "/posts", Post{Title: "Hello", Body: "World"}).
        Scan(&created)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(created.ID)
}

Usage Guide

Creating a Client

A Client is bound to a base URL and reused across requests. Create it once and share it freely β€” it is safe for concurrent use.

client, err := fetch.New("https://api.example.com")

With options:

client, err := fetch.New("https://api.example.com",
    // Total timeout per request (default: 30s)
    fetch.WithTimeout(15*time.Second),

    // Headers sent with every request
    fetch.WithDefaultHeaders(map[string]string{
        "Accept":     "application/json",
        "User-Agent": "my-app/1.0",
    }),

    // Supply your own *http.Client (custom TLS, proxy, cookie jar, etc.)
    fetch.WithHTTPClient(myHTTPClient),

    // Disable automatic *FetchError for non-2xx responses
    fetch.WithoutErrorOnStatus(),
)

Making Requests

Every method returns a *Request builder. Nothing is sent until you call Do() or Scan().


// GET
resp, err := client.Get("/users").Do()

// POST with JSON body
resp, err := client.Post("/users", User{Name: "Alice"}).Do()

// PUT
resp, err := client.Put("/users/1", User{Name: "Alice Updated"}).Do()

// PATCH
resp, err := client.Patch("/users/1", map[string]string{"name": "Alice"}).Do()

// DELETE (body is optional β€” pass nil)
resp, err := client.Delete("/users/1", nil).Do()

// HEAD
resp, err := client.Head("/users").Do()

// OPTIONS
resp, err := client.Options("/users").Do()


// With context
ctx := context.Background()

resp, err := client.GetWithContext(ctx, "/users").Do()

// POST with JSON body
resp, err := client.PostWithContext(ctx, "/users", User{Name: "Alice"}).Do()

// PUT
resp, err := client.PutWithContext(ctx, "/users/1", User{Name: "Alice Updated"}).Do()

// PATCH
resp, err := client.PatchWithContext(ctx, "/users/1", map[string]string{"name": "Alice"}).Do()

// DELETE (body is optional β€” pass nil)
resp, err := client.DeleteWithContext(ctx, "/users/1", nil).Do()

Query Parameters
// Set multiple at once
resp, err := client.Get("/search").
    WithParams(map[string]string{
        "q":     "golang",
        "page":  "1",
        "limit": "20",
    }).
    Do()

// Or set individually
resp, err := client.Get("/search").
    WithParam("q", "golang").
    WithParam("page", "1").
    Do()

// Resulting URL: https://api.example.com/search?limit=20&page=1&q=golang

Headers & Auth
// Set multiple headers
resp, err := client.Get("/data").
    WithHeaders(map[string]string{
        "X-Request-ID": "abc-123",
        "X-Tenant-ID":  "acme",
    }).
    Do()

// Set a single header
resp, err := client.Get("/data").
    WithHeader("X-Request-ID", "abc-123").
    Do()

// Bearer token shorthand
resp, err := client.Get("/profile").
    WithBearerToken("your-jwt-token").
    Do()

// HTTP Basic Auth
resp, err := client.Get("/admin").
    WithBasicAuth("username", "password").
    Do()

Per-request headers always override client-level default headers for the same key.


Request Bodies

JSON (default for Post, Put, Patch):

type CreateUserReq struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}

resp, err := client.Post("/users", CreateUserReq{
    Name:  "Alice",
    Email: "alice@example.com",
}).Do()

Override / replace the body at any point in the chain:

// JSON
resp, err := client.Post("/data", nil).
    WithJSONBody(map[string]any{"key": "value"}).
    Do()

// URL-encoded form
resp, err := client.Post("/login", nil).
    WithFormBody(map[string]string{
        "username": "alice",
        "password": "s3cr3t",
    }).
    Do()

// Multipart form (file upload)
fileBytes, _ := os.ReadFile("avatar.png")
resp, err := client.Post("/upload", nil).
    WithMultipartBody(map[string]any{
        "avatar": fileBytes,  // []byte β†’ file field
        "caption": "My photo", // string β†’ text field
    }).
    Do()

// Plain text
resp, err := client.Post("/logs", nil).
    WithTextBody("something happened at 12:00").
    Do()

// Raw reader (e.g. a file, a buffer)
f, _ := os.Open("data.bin")
defer f.Close()
resp, err := client.Post("/upload", nil).
    WithRawBody(f, "application/octet-stream").
    Do()

Reading Responses

Scan β€” the fastest path for JSON APIs:

var user User
// Executes the request AND decodes the JSON body in one call.
err := client.Get("/users/1").Scan(&user)

Do β€” when you need the response itself:

resp, err := client.Get("/users/1").Do()
if err != nil {
    return err
}

// Decode options (call exactly one β€” each closes the body)
var user User
err = resp.JSON(&user)      // JSON β†’ struct
text, err := resp.Text()    // body as string
raw, err := resp.Bytes()    // body as []byte

// Status helpers
resp.IsOK()           // 2xx
resp.IsRedirect()     // 3xx
resp.IsClientError()  // 4xx
resp.IsServerError()  // 5xx

// Underlying *http.Response (body not yet read)
resp.StatusCode   // int
resp.Status       // "200 OK"
resp.Header       // http.Header
resp.Raw          // *http.Response

Error Handling

By default, any non-2xx response is returned as *FetchError. This means you never need to check resp.IsOK() manually.

var user User
err := client.Get("/users/99").Scan(&user)
if err != nil {
    if fe, ok := fetch.AsFetchError(err); ok {
        // HTTP-level error
        fmt.Println(fe.StatusCode) // e.g. 404
        fmt.Println(fe.Status)     // e.g. "404 Not Found"
        fmt.Println(string(fe.Body)) // raw response body

        switch {
        case fe.IsNotFound():
            // handle 404
        case fe.IsUnauthorized():
            // handle 401 β€” refresh token, redirect to login, etc.
        case fe.IsForbidden():
            // handle 403
        case fe.IsServerError():
            // handle 5xx
        }
    }
    // Network / timeout / interceptor error
    return err
}

Opt out of automatic error wrapping (handle every status yourself):

client, _ := fetch.New("https://api.example.com",
    fetch.WithoutErrorOnStatus(),
)

resp, err := client.Get("/maybe-404").Do()
if err != nil {
    return err // only network errors reach here
}
if !resp.IsOK() {
    // decide what to do with 4xx / 5xx
}

Interceptors

Interceptors run on every request/response made by a client β€” ideal for cross-cutting concerns like logging, auth token injection, and metrics.

// ── Request interceptor ───────────────────────────────────────────────────
// Injects a correlation ID into every outgoing request.
client.UseRequest(func(req *http.Request) (*http.Request, error) {
    req.Header.Set("X-Request-ID", uuid.New().String())
    return req, nil
})

// Refreshes an expired bearer token transparently.
client.UseRequest(func(req *http.Request) (*http.Request, error) {
    token, err := tokenStore.Valid()
    if err != nil {
        return nil, err // aborts the request
    }
    req.Header.Set("Authorization", "Bearer "+token)
    return req, nil
})

// ── Response interceptor ──────────────────────────────────────────────────
// Structured request logging.
client.UseResponse(func(resp *http.Response) (*http.Response, error) {
    log.Printf("[fetch] %s %s β†’ %s",
        resp.Request.Method,
        resp.Request.URL.Path,
        resp.Status,
    )
    return resp, nil
})

// Multiple interceptors are executed in registration order.
client.UseRequest(interceptorA, interceptorB, interceptorC)

Package-level API

For scripts and one-off requests where creating a Client is overkill, use the package-level functions. They work without a base URL.

// No client needed β€” just pass a full URL.
var post Post
err := fetch.Get("https://jsonplaceholder.typicode.com/posts/1").
    Scan(&post)

err = fetch.Post("https://example.com/events",
    map[string]string{"event": "signup"},
).Do()

// Adjust the default timeout globally.
fetch.SetDefaultTimeout(5 * time.Second)

API Reference

Client
Method Description
fetch.New(baseURL, ...Option) Create a new client
client.UseRequest(...fn) Register request interceptor(s)
client.UseResponse(...fn) Register response interceptor(s)
Options
Option Description Default
WithTimeout(d) Request timeout 30s
WithHTTPClient(c) Custom *http.Client built-in
WithDefaultHeaders(m) Headers sent with every request β€”
WithoutErrorOnStatus() Disable *FetchError for non-2xx enabled
*Request (builder)
Method Description
WithParam(key, value) Set a single query parameter
WithParams(map) Set multiple query parameters
WithHeader(key, value) Set a single header
WithHeaders(map) Set multiple headers
WithBearerToken(token) Set Authorization: Bearer <token>
WithBasicAuth(user, pass) Set HTTP Basic Auth header
WithJSONBody(v) Set a JSON-encoded body
WithFormBody(map) Set a URL-encoded form body
WithMultipartBody(map) Set a multipart/form-data body
WithTextBody(s) Set a plain-text body
WithRawBody(r, contentType) Set an arbitrary io.Reader body
Do() Execute β†’ (*Response, error)
Scan(v) Execute + JSON-decode β†’ error
*Response
Method Description
JSON(v) Decode body as JSON into v
XML(v) Decode body as XML into v
Text() Read body as string
Bytes() Read body as []byte
IsOK() true for 2xx
IsRedirect() true for 3xx
IsClientError() true for 4xx
IsServerError() true for 5xx
*FetchError
Field / Method Description
StatusCode int HTTP status code
Status string HTTP status string (e.g. "404 Not Found")
Body []byte Raw response body
Header http.Header Response headers
IsNotFound() true for 404
IsUnauthorized() true for 401
IsForbidden() true for 403
IsServerError() true for 5xx
fetch.AsFetchError(err) Unwrap any error to *FetchError

Contributing

Contributions are welcome and appreciated. Please follow these steps.

1. Fork & clone
git clone https://github.com/rixotech/fetch-go.git
cd fetch
2. Create a feature branch
git checkout -b feat/your-feature-name
# or for bug fixes:
git checkout -b fix/what-was-broken
3. Make your changes
  • Keep the public API backward compatible unless the change is intentional and documented.
  • Add or update tests for every changed behaviour.
  • Ensure all exported symbols have Go doc comments.
4. Run tests and checks
# All tests must pass
go test ./... -race -count=1

# No vet warnings
go vet ./...

# Format your code
gofmt -w .
5. Commit with a clear message

We follow Conventional Commits:

git commit -m "feat: add WithRetry option for automatic retries"
git commit -m "fix: nil pointer when base URL has trailing slash"
git commit -m "docs: add multipart upload example to README"
git commit -m "test: cover 401 interceptor refresh scenario"
6. Open a Pull Request

Push your branch and open a PR against main. Include:

  • What the change does
  • Why it is needed
  • Any breaking changes (if applicable)
Reporting bugs

Open an issue at github.com/rixotech/fetch-go/issues and include:

  • Go version (go version)
  • OS and architecture
  • A minimal code snippet that reproduces the bug
  • Expected vs actual behaviour
Suggesting features

Open a GitHub Discussion before opening a PR for large changes, so the design can be agreed on first.


Changelog

v0.1.0 β€” Initial Release
  • Fluent request builder with full HTTP verb support
  • JSON, form, multipart, text, and raw body types
  • Typed *FetchError with status helpers
  • Request and response interceptor chains
  • WithBearerToken and WithBasicAuth auth helpers
  • Scan(v) one-liner for JSON decode
  • Package-level convenience API

License

Released under the MIT License. Copyright (c) 2026 RixoTech.


Made with β˜• and Go Β· Documentation Β· Report a Bug

Documentation ΒΆ

Index ΒΆ

Examples ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

This section is empty.

Functions ΒΆ

func SetDefaultTimeout ΒΆ

func SetDefaultTimeout(d time.Duration)

SetDefaultTimeout changes the timeout of the package-level default client.

Types ΒΆ

type Client ΒΆ

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

Client is a reusable HTTP client bound to a base URL β€” the Go equivalent of an axios instance. Create one via New; share it across goroutines freely.

Any error that occurs during construction (e.g. an invalid base URL) is stored inside the Client and returned the first time Do or Scan is called. Call client.Error() to inspect it eagerly before making any requests.

func New ΒΆ

func New(baseURL string, opts ...Option) *Client

New creates a Client bound to baseURL. It never returns an error directly β€” any construction error (e.g. an invalid URL) is stored inside the Client and surfaced the first time Do or Scan is called on any request built from it.

Check eagerly with client.Error() if you need to fail fast at startup.

client := fetch.New("https://api.example.com")
if err := client.Error(); err != nil {
    log.Fatal(err)
}
Example ΒΆ

ExampleNew demonstrates creating a reusable client with common defaults.

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/rixotech/fetch-go"
)

// ExampleNew demonstrates creating a reusable client with common defaults.
func main() {
	client := fetch.New("https://api.example.com",
		fetch.WithTimeout(10*time.Second),
		fetch.WithDefaultHeaders(map[string]string{
			"Accept":     "application/json",
			"User-Agent": "my-app/1.0",
		}),
	)

	// Attach a request interceptor that injects an auth token on every call.
	client.UseRequest(func(req *http.Request) (*http.Request, error) {
		req.Header.Set("Authorization", "Bearer "+getToken())
		return req, nil
	})

	// Attach a response interceptor for structured logging.
	client.UseResponse(func(resp *http.Response) (*http.Response, error) {
		fmt.Printf("[fetch] %s %s β†’ %s\n", resp.Request.Method, resp.Request.URL.Path, resp.Status)
		return resp, nil
	})

	ctx := context.Background()

	// ── GET with query params ─────────────────────────────────────────────
	var users []struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}
	client.Get(ctx, "/users").
		WithParam("page", "1").
		WithParam("limit", "20").
		Scan(&users)
	if err := client.Error(); err != nil {
		// Non-2xx responses surface as *fetch.FetchError.
		if fe, ok := fetch.AsFetchError(err); ok {
			fmt.Printf("HTTP %d: %s\n", fe.StatusCode, fe.Body)
		}
		log.Fatal(err)
	}

	// ── POST with JSON body ───────────────────────────────────────────────
	type CreateUserReq struct {
		Name  string `json:"name"`
		Email string `json:"email"`
	}
	type User struct {
		ID    int    `json:"id"`
		Name  string `json:"name"`
		Email string `json:"email"`
	}

	var created User
	client.Post(ctx, "/users", CreateUserReq{
		Name:  "Alice",
		Email: "alice@example.com",
	}).Scan(&created)
	if err := client.Error(); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("created user id=%d\n", created.ID)

	// ── PUT ───────────────────────────────────────────────────────────────
	var updated User
	client.Put(ctx, "/users/1", CreateUserReq{Name: "Alice Updated"}).
		Scan(&updated)
	if err := client.Error(); err != nil {
		log.Fatal(err)
	}

	// ── PATCH ─────────────────────────────────────────────────────────────
	client.Patch(ctx, "/users/1", map[string]string{"name": "Alice V2"}).
		Scan(&updated)
	if err := client.Error(); err != nil {
		log.Fatal(err)
	}

	// ── DELETE ────────────────────────────────────────────────────────────
	resp, err := client.Delete(ctx, "/users/1", nil).Do()
	if err := client.Error(); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("delete status: %s\n", resp.Status)

	// ── Form-encoded body ─────────────────────────────────────────────────
	_, err = client.Post(ctx, "/login", nil).
		WithFormBody(map[string]string{
			"username": "alice",
			"password": "s3cr3t",
		}).Do()
	if err != nil {
		log.Fatal(err)
	}

	// ── Raw text body ──────────────────────────────────────────────────────
	_, err = client.Post(ctx, "/logs", nil).
		WithTextBody("something happened").
		Do()
	if err != nil {
		log.Fatal(err)
	}

	// ── Read response as plain text ────────────────────────────────────────
	r, err := client.Get(ctx, "/health").Do()
	if err != nil {
		log.Fatal(err)
	}
	text, _ := r.Text()
	fmt.Println(text)
}

func getToken() string { return "secret-token" }

func (*Client) Delete ΒΆ

func (c *Client) Delete(ctx context.Context, path string, v any) *Request

Delete returns a DELETE Request. An optional body may be passed (nil is fine).

func (*Client) Error ΒΆ added in v0.3.0

func (c *Client) Error() error

Error returns the first error that occurred during client construction, or nil if the client was created successfully.

Use this when you want to validate the client at startup rather than waiting for the first request to fail:

client := fetch.New(os.Getenv("API_URL"))
if err := client.Error(); err != nil {
    log.Fatal(err)
}

func (*Client) Get ΒΆ

func (c *Client) Get(ctx context.Context, path string) *Request

Get returns a GET Request for the given path.

func (*Client) Head ΒΆ

func (c *Client) Head(ctx context.Context, path string) *Request

Head returns a HEAD Request.

func (*Client) Options ΒΆ

func (c *Client) Options(ctx context.Context, path string) *Request

Options returns an OPTIONS Request.

func (*Client) Patch ΒΆ

func (c *Client) Patch(ctx context.Context, path string, v any) *Request

Patch returns a PATCH Request with a JSON-encoded body.

func (*Client) Post ΒΆ

func (c *Client) Post(ctx context.Context, path string, v any) *Request

Post returns a POST Request with a JSON-encoded body.

func (*Client) Put ΒΆ

func (c *Client) Put(ctx context.Context, path string, v any) *Request

Put returns a PUT Request with a JSON-encoded body.

func (*Client) UseRequest ΒΆ

func (c *Client) UseRequest(fns ...RequestInterceptorFn)

UseRequest appends one or more request interceptors. They are executed in registration order before every outgoing request.

func (*Client) UseResponse ΒΆ

func (c *Client) UseResponse(fns ...ResponseInterceptorFn)

UseResponse appends one or more response interceptors. They are executed in registration order after every response is received.

type FetchError ΒΆ

type FetchError struct {
	StatusCode int
	Status     string
	Body       []byte
	Header     http.Header
}

FetchError represents an HTTP-level error (non-2xx response). It is distinct from a network/transport error so callers can branch on response status without string-matching.

func AsFetchError ΒΆ

func AsFetchError(err error) (*FetchError, bool)

AsFetchError unwraps err into *FetchError. Returns (nil, false) when err is not (or does not wrap) a *FetchError.

func (*FetchError) Error ΒΆ

func (e *FetchError) Error() string

func (*FetchError) IsForbidden ΒΆ

func (e *FetchError) IsForbidden() bool

IsForbidden returns true when the server returned 403.

func (*FetchError) IsNotFound ΒΆ

func (e *FetchError) IsNotFound() bool

IsNotFound returns true when the server returned 404.

func (*FetchError) IsServerError ΒΆ

func (e *FetchError) IsServerError() bool

IsServerError returns true for any 5xx status.

func (*FetchError) IsUnauthorized ΒΆ

func (e *FetchError) IsUnauthorized() bool

IsUnauthorized returns true when the server returned 401.

type Option ΒΆ

type Option func(*Client)

Option is a functional option for Client.

func WithDefaultHeaders ΒΆ

func WithDefaultHeaders(h map[string]string) Option

WithDefaultHeaders merges h into the client's default headers. Per-request headers always take precedence over these.

func WithHTTPClient ΒΆ

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient replaces the underlying *http.Client entirely. Useful when you need custom TLS config, a proxy, or cookie jars.

func WithTimeout ΒΆ

func WithTimeout(d time.Duration) Option

WithTimeout sets the total timeout for every request made by this client. Default: 30 s.

func WithoutErrorOnStatus ΒΆ

func WithoutErrorOnStatus() Option

WithoutErrorOnStatus disables automatic *FetchError wrapping for non-2xx responses. Use when you want to inspect every status code yourself.

type Request ΒΆ

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

Request is a single pending HTTP request built via the fluent API. Call Do or Scan to execute it.

func Delete ΒΆ

func Delete(rawURL string, v any) *Request

Delete issues a one-off DELETE request without a base URL.

func DeleteWithContext ΒΆ added in v1.0.0

func DeleteWithContext(ctx context.Context, rawURL string, v any) *Request

Delete issues a one-off DELETE request without a base URL.

func Get ΒΆ

func Get(rawURL string) *Request

Get issues a one-off GET request without a base URL.

func GetWithContext ΒΆ added in v1.0.0

func GetWithContext(ctx context.Context, rawURL string) *Request

Get issues a one-off GET request without a base URL.

func Patch ΒΆ

func Patch(rawURL string, v any) *Request

Patch issues a one-off PATCH request without a base URL.

func PatchWithContext ΒΆ added in v1.0.0

func PatchWithContext(ctx context.Context, rawURL string, v any) *Request

Patch issues a one-off PATCH request without a base URL.

func Post ΒΆ

func Post(rawURL string, v any) *Request

Post issues a one-off POST request without a base URL.

func PostWithContext ΒΆ added in v1.0.0

func PostWithContext(ctx context.Context, rawURL string, v any) *Request

Post issues a one-off POST request without a base URL.

func Put ΒΆ

func Put(rawURL string, v any) *Request

Put issues a one-off PUT request without a base URL.

func PutWithContext ΒΆ added in v1.0.0

func PutWithContext(ctx context.Context, rawURL string, v any) *Request

Put issues a one-off PUT request without a base URL.

func (*Request) Do ΒΆ

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

Do executes the request and returns the raw *Response.

Unless WithoutErrorOnStatus was set on the client, a *FetchError is returned for any non-2xx status code so the caller does not need to check resp.IsOK() manually.

func (*Request) Scan ΒΆ

func (r *Request) Scan(v any) error

Scan executes the request and JSON-decodes a successful response body into v. It is shorthand for:

resp, err := r.Do()
if err != nil { return err }
return resp.JSON(v)

func (*Request) WithBasicAuth ΒΆ

func (r *Request) WithBasicAuth(username, password string) *Request

WithBasicAuth sets the Authorization header using HTTP Basic Auth encoding.

func (*Request) WithBearerToken ΒΆ

func (r *Request) WithBearerToken(token string) *Request

WithBearerToken is a convenience shortcut for Authorization: Bearer <token>.

func (*Request) WithFormBody ΒΆ

func (r *Request) WithFormBody(fields map[string]string) *Request

WithFormBody replaces the request body with URL-encoded form data and sets Content-Type: application/x-www-form-urlencoded.

func (*Request) WithHeader ΒΆ

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

WithHeader sets a single HTTP header.

func (*Request) WithHeaders ΒΆ

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

WithHeaders merges HTTP headers into the request. Per-request headers override client-level default headers.

func (*Request) WithJSONBody ΒΆ

func (r *Request) WithJSONBody(v any) *Request

WithJSONBody replaces the request body with a JSON-encoded v. It also sets Content-Type: application/json.

func (*Request) WithMultipartBody ΒΆ

func (r *Request) WithMultipartBody(fields map[string]any) *Request

WithMultipartBody replaces the request body with a multipart/form-data payload. Pass file content as []byte values; all other values are treated as text fields.

func (*Request) WithParam ΒΆ

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

WithParam sets a single query parameter.

func (*Request) WithParams ΒΆ

func (r *Request) WithParams(params map[string]string) *Request

WithParams merges query parameters into the request URL. May be called multiple times; later values overwrite earlier ones for the same key.

r.WithParams(map[string]string{"page": "2", "limit": "50"})

func (*Request) WithRawBody ΒΆ

func (r *Request) WithRawBody(reader io.Reader, contentType string) *Request

WithRawBody sets an arbitrary body reader and Content-Type.

func (*Request) WithTextBody ΒΆ

func (r *Request) WithTextBody(text string) *Request

WithTextBody replaces the request body with a plain-text string.

type RequestInterceptorFn ΒΆ

type RequestInterceptorFn func(req *http.Request) (*http.Request, error)

RequestInterceptorFn is called just before an HTTP request is sent. It may mutate or replace the request. Returning a non-nil error aborts the request and surfaces the error from Do / Scan.

type Response ΒΆ

type Response struct {
	// StatusCode is the HTTP response status code (e.g. 200, 404).
	StatusCode int
	// Status is the raw status string (e.g. "200 OK").
	Status string
	// Header contains the response headers.
	Header http.Header
	// Raw is the underlying *http.Response for callers who need lower-level
	// access. Body has NOT been consumed; call exactly one of the decode
	// helpers, or read Body yourself and close it.
	Raw *http.Response
}

Response wraps *http.Response and exposes ergonomic decode helpers. The underlying body is always closed after any Scan / JSON / Text / Bytes call β€” callers must not read the body a second time.

func (*Response) Bytes ΒΆ

func (r *Response) Bytes() ([]byte, error)

Bytes reads and returns the raw response body bytes.

func (*Response) IsClientError ΒΆ

func (r *Response) IsClientError() bool

IsClientError returns true for any 4xx status code.

func (*Response) IsOK ΒΆ

func (r *Response) IsOK() bool

IsOK returns true for any 2xx status code.

func (*Response) IsRedirect ΒΆ

func (r *Response) IsRedirect() bool

IsRedirect returns true for any 3xx status code.

func (*Response) IsServerError ΒΆ

func (r *Response) IsServerError() bool

IsServerError returns true for any 5xx status code.

func (*Response) JSON ΒΆ

func (r *Response) JSON(v any) error

JSON decodes the response body as JSON into v.

func (*Response) Text ΒΆ

func (r *Response) Text() (string, error)

Text reads and returns the response body as a UTF-8 string.

func (*Response) XML ΒΆ

func (r *Response) XML(v any) error

XML decodes the response body as XML into v.

type ResponseInterceptorFn ΒΆ

type ResponseInterceptorFn func(resp *http.Response) (*http.Response, error)

ResponseInterceptorFn is called immediately after a response is received (but before the body is read / decoded). It may mutate or replace the response. Returning a non-nil error surfaces the error from Do / Scan.

Directories ΒΆ

Path Synopsis

Jump to

Keyboard shortcuts

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