DocsHTTPMaking requests

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.