go-client
This repository contains a generic HTTP client which can be adapted to provide:
- General HTTP methods for GET and POST of data
- Ability to send and receive JSON, plaintext and XML data
- Ability to send files and data of type
multipart/form-data
- Ability to send data of type
application/x-www-form-urlencoded
- Debugging capabilities to see the request and response data
- Streaming text and JSON events
API Documentation: https://pkg.go.dev/github.com/mutablelogic/go-client
There are also some example clients which use this library:
Aiming to have compatibility with go version 1.21 and above.
Basic Usage
The following example shows how to decode a response from a GET request
to a JSON endpoint:
package main
import (
client "github.com/mutablelogic/go-client"
)
func main() {
c := client.New(client.OptEndpoint("https://api.example.com/api/v1"))
var response struct {
Message string `json:"message"`
}
if err := c.Do(nil, &response, OptPath("test")); err != nil {
}
fmt.Println(response.Message)
}
Various options can be passed to the client New
method to control its behaviour:
OptEndpoint(value string)
sets the endpoint for all requests
OptTimeout(value time.Duration)
sets the timeout on any request, which defaults to 30 seconds.
Timeouts can be ignored on a request-by-request basis using the OptNoTimeout
option (see below).
OptUserAgent(value string)
sets the user agent string on each API request.
OptTrace(w io.Writer, verbose bool)
allows you to debug the request and response data.
When verbose
is set to true, it also displays the payloads.
OptStrict()
turns on strict content type checking on anything returned from the API.
OptRateLimit(value float32)
sets the limit on number of requests per second and the API
will sleep to regulate the rate limit when exceeded.
OptReqToken(value Token)
sets a request token for all client requests. This can be
overridden by the client for individual requests using OptToken
(see below).
OptSkipVerify()
skips TLS certificate domain verification.
OptHeader(key, value string)
appends a custom header to each request.
Usage with a payload
The first argument to the Do
method is the payload to send to the server, when set.
You can create a payload using the following methods:
client.NewRequest()
returns a new empty payload which defaults to GET.
client.NewJSONRequest(payload any, accept string)
returns a new request with
a JSON payload which defaults to POST.
client.NewMultipartRequest(payload any, accept string)
returns a new request with
a Multipart Form data payload which defaults to POST.
client.NewFormRequest(payload any, accept string)
returns a new request with a
Form data payload which defaults to POST.
For example,
package main
import (
client "github.com/mutablelogic/go-client"
)
func main() {
c := client.New(client.OptEndpoint("https://api.example.com/api/v1"))
var request struct {
Prompt string `json:"prompt"`
}
var response struct {
Reply string `json:"reply"`
}
request.Prompt = "Hello, world!"
payload := client.NewJSONRequest(request)
if err := c.Do(payload, &response, OptPath("test")); err != nil {
}
fmt.Println(response.Reply)
}
You can also implement your own payload by implementing the Payload
interface:
type Payload interface {
io.Reader
Method() string
Type() string
Accept() string
}
Request options
The signature of the Do
method is as follows:
type Client interface {
Do(in Payload, out any, opts ...RequestOpt) error
DoWithContext(ctx context.Context, in Payload, out any, opts ...RequestOpt) error
}
If you pass a context to the DoWithContext
method, then the request can be
cancelled using the context in addition to the timeout. Various options can be passed to
modify each individual request when using the Do
method:
OptReqEndpoint(value string)
sets the endpoint for the request
OptPath(value ...string)
appends path elements onto a request endpoint
OptToken(value Token)
adds an authorization header (overrides the client OptReqToken option)
OptQuery(value url.Values)
sets the query parameters to a request
OptHeader(key, value string)
sets a custom header to the request
OptNoTimeout()
disables the timeout on the request, which is useful for long running requests
OptTextStreamCallback(func(TextStreamCallback) error)
allows you to set a callback
function to process a streaming text response of type text/event-stream
. See below for
more details.
OptJsonStreamCallback(func(any) error)
allows you to set a callback for JSON streaming
responses. The callback should have the signature func(any) error
. See below for
more details.
Authentication
The authentication token can be set as follows:
package main
import (
client "github.com/mutablelogic/go-client"
)
func main() {
c := client.New(
client.OptEndpoint("https://api.example.com/api/v1"),
client.OptReqToken(client.Token{
Scheme: "Bearer",
Value: os.GetEnv("API_TOKEN"),
}),
)
}
You can also set the token on a per-request basis using the OptToken
option in call to the Do
method.
Form submission
You can create a payload with form data:
client.NewFormRequest(payload any, accept string)
returns a new request with a Form
data payload which defaults to POST.
client.NewMultipartRequest(payload any, accept string)
returns a new request with
a Multipart Form data payload which defaults to POST. This is useful for file uploads.
The payload should be a struct
where the fields are converted to form tuples. File uploads require a field of type multipart.File
. For example,
package main
import (
client "github.com/mutablelogic/go-client"
multipart "github.com/mutablelogic/go-client/pkg/multipart"
)
type FileUpload struct {
File multipart.File `json:"file"`
}
func main() {
c := client.New(client.OptEndpoint("https://api.example.com/api/v1"))
request := FileUpload{
File: multipart.File{
Path: "helloworld.txt",
Body: strings.NewReader("Hello, world!"),
}
}
if payload, err := client.NewMultipartRequest(request, "*/*"); err != nil {
} else if err := c.Do(payload, &response, OptPath("upload")); err != nil {
}
}
Unmarshalling responses
You can implement your own unmarshalling of responses by implementing the Unmarshaler
interface:
type Unmarshaler interface {
Unmarshal(header http.Header, r io.Reader) error
}
The first argument to the Unmarshal
method is the HTTP header of the response, and the second
argument is the body of the response. The method should return an error if the unmarshalling fails.
Text Streaming Responses
The client implements a streaming text event callback which can be used to process a stream of text events,
as per the Mozilla specification.
In order to process streamed events, pass the OptTextStreamCallback()
option to the request
with a callback function, which should have the following signature:
func Callback(event client.TextStreamEvent) error {
if event.Event == "close" {
return io.EOF
}
var data map[string]any
if err := event.Json(data); err != nil {
return err
}
return nil
}
The TextStreamEvent
object has the following
If you return an error of type io.EOF
from the callback, then the stream will be closed.
Similarly, if you return any other error the stream will be closed and the error returned.
Usually, you would pair this option with OptNoTimeout
to prevent the request from timing out.
JSON Streaming Responses
The client decodes JSON streaming responses by passing a callback function to the OptJsonStreamCallback()
option.
The callback with signature func(any) error
is called for each JSON object in the stream, where the argument
is the same type as the object in the request.
You can return an error from the callback to stop the stream and return the error, or return io.EOF
to stop the stream
immediately and return success.