Go HTTP Client
Go's net/http package includes a built-in HTTP client for making requests to external APIs and web services. The client handles GET, POST, PUT, DELETE, and other HTTP methods. It manages connections, redirects, and timeouts automatically.
Making a GET Request
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
resp, err := http.Get("https://httpbin.org/get")
if err != nil {
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close() // always close the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading body:", err)
return
}
fmt.Println("Status:", resp.Status)
fmt.Println("Body:", string(body))
}
HTTP Client Flow
Go Program
│
│ http.Get(url)
▼
HTTP Request ──────────────────► External Server
│ │
│ │ processes request
│ response (status + body) │
◄───────────────────────────────────┘
│
▼
resp.Body → io.ReadAll() → []byte → string
Reading the Response
| Field / Method | Description |
|---|---|
resp.Status | Status text, e.g. "200 OK" |
resp.StatusCode | Status number, e.g. 200 |
resp.Header | Map of response headers |
resp.Body | Response body — must be closed after reading |
Making a POST Request with JSON
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
user := User{Name: "Alice", Email: "alice@example.com"}
jsonData, err := json.Marshal(user)
if err != nil {
fmt.Println("JSON error:", err)
return
}
resp, err := http.Post(
"https://httpbin.org/post",
"application/json",
bytes.NewBuffer(jsonData),
)
if err != nil {
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println("Status:", resp.StatusCode)
fmt.Println("Response:", string(body))
}
Using a Custom HTTP Client with Timeout
The default http.Get has no timeout — it can wait forever. Always use a custom client with a timeout for production code.
package main
import (
"fmt"
"io"
"net/http"
"time"
)
func main() {
client := &http.Client{
Timeout: 5 * time.Second, // cancel request after 5 seconds
}
resp, err := client.Get("https://httpbin.org/delay/2")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Setting Custom Headers
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, err := http.NewRequest("GET", "https://httpbin.org/headers", nil)
if err != nil {
fmt.Println("Request error:", err)
return
}
req.Header.Set("Authorization", "Bearer mytoken123")
req.Header.Set("Accept", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
Parsing JSON from a Response
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
}
func main() {
resp, err := http.Get("https://jsonplaceholder.typicode.com/posts/1")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
var post Post
json.NewDecoder(resp.Body).Decode(&post)
fmt.Println("ID:", post.ID)
fmt.Println("Title:", post.Title)
}
HTTP Methods Summary
| Method | Go Function | Purpose |
|---|---|---|
| GET | http.Get(url) | Retrieve data |
| POST | http.Post(url, contentType, body) | Send data to create a resource |
| PUT / PATCH / DELETE | http.NewRequest(method, url, body) | Custom method with full control |
Key Points
- Always call
defer resp.Body.Close()after receiving a response to avoid resource leaks - Use a custom
http.Clientwith aTimeoutto prevent requests from hanging forever - Use
http.NewRequestwhen custom headers or non-standard methods are needed - Use
json.NewDecoder(resp.Body).Decode(&target)to parse JSON responses directly - Always check
resp.StatusCodeto verify the server processed the request successfully
