Skip to content

Requester (HTTP client)

core.IRequester — outbound HTTP, backed by go-resty. ctx.Requester() reuses a shared client (connection pool kept alive).

The API is deliberately thin: you build requests with the full go-resty API via R(), and run them with Send, which is the only thing the framework wraps — it binds the request context and converts every failure (transport error or non-2xx status) into a core.IError.

Basic usage

go
r := ctx.Requester()

var out UserDTO
resp, err := r.Send(r.R().SetResult(&out), http.MethodGet, "https://api/users/1")
if err != nil {
    return err // IError — return it straight from a handler
}
_ = resp // *resty.Response (status, headers, raw body, trace info)

err is a core.IError:

  • transport failure → code NETWORK_ERROR, status 500
  • non-2xx status → the response status, reusing the remote code/message when the JSON body provides them
  • success → nil

Full go-resty when building

R() returns a *resty.Request already bound to the context, so anything resty offers is available; Send just executes and wraps the error:

go
// headers, query, auth, typed result
var out UserDTO
resp, err := r.Send(
    r.R().
        SetHeader("X-Token", token).
        SetQueryParam("q", "search").
        SetAuthToken(bearer).
        SetResult(&out),
    http.MethodGet, url,
)

// JSON body
resp, err = r.Send(r.R().SetBody(payload).SetResult(&out), http.MethodPost, url)

// multipart file upload
resp, err = r.Send(
    r.R().
        SetFileReader("avatar", "a.png", reader).
        SetFormData(map[string]string{"user_id": "1"}),
    http.MethodPost, url,
)

// form-urlencoded
resp, err = r.Send(
    r.R().SetFormData(map[string]string{"grant_type": "client_credentials"}),
    http.MethodPost, tokenURL,
)

// streaming a large response
resp, err = r.Send(r.R().SetDoNotParseResponse(true), http.MethodGet, fileURL)
defer resp.RawBody().Close()

Interface

go
type IRequester interface {
    R() *resty.Request                                                  // full go-resty builder, ctx-bound
    Send(req *resty.Request, method, url string) (*resty.Response, IError) // execute → IError
    WithContext(ctx context.Context) IRequester
    Resty() *resty.Client                                               // shared client — configure at startup
}

Client-level configuration (retries, TLS, base URL, middleware)

Configure the shared resty client once at startup and wire it in:

go
rc := resty.New().
    SetTimeout(10 * time.Second).
    SetRetryCount(3).
    SetBaseURL("https://api.example.com").
    OnBeforeRequest(func(c *resty.Client, req *resty.Request) error { /* trace */ return nil })

app, _ := core.NewApp(env, core.WithRequester(core.NewRequesterWithClient(rc)))

Then ctx.Requester() uses that client, still ctx-bound per request.

Custom timeout

go
bg, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
r := ctx.Requester().WithContext(bg)
r.Send(r.R(), http.MethodGet, url)

Maintained by Passakon Puttasuwan & Dev Core Team.