Making requests
Construct a client and send GET, POST, and JSON requests, then read the buffered response.
Every request follows the same shape. Build a Client once, then call the method for the verb you want with a context and a request struct. This guide covers the buffered verbs and the JSON helpers. Streaming has its own guide.
Construct a client
NewClient takes a Config and returns a Client. The client is safe to reuse across goroutines, so build it once at startup and hand it to whatever needs it.
client := http.NewClient(http.Config{
BaseURL: "https://api.example.com",
UserAgent: "demo/1.0.0",
Platform: "cli",
})BaseURL is joined to each request's Path, so a call to /users/1 hits https://api.example.com/users/1. Leave BaseURL empty to pass a full URL as the path instead. The identity fields seed headers sent on every request, covered in Configuring the client.
GET
Get takes a GetRequest, which wraps the shared Request. The response body is read into resp.Body as a []byte.
resp, err := client.Get(ctx, http.GetRequest{
Request: http.Request{Path: "/users/1"},
})
if err != nil {
return err
}
fmt.Println(resp.StatusCode, string(resp.Body))POST with a body
The body-carrying verbs (Post, Put, Patch) take a request that embeds Request and adds a Body []byte. Marshal your payload with the JSON helper, which returns the encoded string and any marshal error.
body, err := http.JSON(map[string]any{
"name": "ada",
"email": "[email protected]",
})
if err != nil {
return err
}
resp, err := client.Post(ctx, http.PostRequest{
Request: http.Request{Path: "/users"},
Body: []byte(body),
})Put and Patch work the same way with PutRequest and PatchRequest. Delete takes a bare Request and sends no body.
Decode the response
FromJSON is a generic helper that unmarshals a response body into a value of the type you ask for.
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
user, err := http.FromJSON[User](resp.Body)
if err != nil {
return err
}
fmt.Println(user.Name)Note
FromJSON decodes whatever bytes it is given, so decode only after you have checked resp.StatusCode. An error body is still valid JSON to the decoder and would unmarshal into a zero-valued struct.
Read the raw response
Response also exposes the status code and the response headers, so you can branch on either.
resp, err := client.Get(ctx, http.GetRequest{Request: http.Request{Path: "/users/1"}})
if err != nil {
return err
}
if resp.StatusCode == 200 {
contentType := resp.Headers.Get("Content-Type")
fmt.Println(contentType, len(resp.Body))
}For per-request paths, query parameters, and headers, see Request options.