Introduction: Why HTTP Client in Go Deserves Attention
Modern web applications rarely work in isolation. Almost every service interacts with external APIs, microservices, or third-party resources via HTTP. The Go language (Golang) provides a powerful built-in library net/http that allows you to create efficient and high-performance HTTP clients without third-party dependencies.
In this article, we will break down how to properly work with the HTTP client in Go: from simple GET requests to error handling and timeout management. You will learn how to avoid common mistakes and write reliable code.
Basics of Working with HTTP Client in Go
The net/http library in Go is designed with performance in mind. Unlike many other languages where each request creates a new connection, Go uses a connection pool by default. This means TCP connections are reused, which significantly speeds up operations.
Simple GET Request
Let's start with the simplest example — sending a GET request using the http.Get() function:
package main
import ( "fmt" "io" "net/http" "os")
func main() { // Simple GET request resp, err := http.Get("https://api.github.com") if err != nil { fmt.Printf("Error executing request: %v", err) os.Exit(1) } defer resp.Body.Close()
// Read response body body, err := io.ReadAll(resp.Body) if err != nil { fmt.Printf("Error reading response: %v", err) os.Exit(1) }
fmt.Printf("Status: %s", resp.Status) fmt.Printf("Response body: %s", string(body))}Important: always close the response body (resp.Body.Close()). Use defer for guaranteed closure, even if an error occurs.
GET Request with Parameters and Headers
For more complex requests, it's better to use the full syntax by creating an http.Request object:
package main
import ( "fmt" "io" "net/http" "time")
func main() { // Create a new client client := &http.Client{ Timeout: 10 * time.Second, }
// Create a request req, err := http.NewRequest("GET", "https://api.example.com/users", nil) if err != nil { fmt.Printf("Error creating request: %v", err) return }
// Add headers req.Header.Set("Authorization", "Bearer your-token-here") req.Header.Set("Accept", "application/json") req.Header.Set("User-Agent", "Go-http-client/1.0")
// Add query parameters q := req.URL.Query() q.Add("limit", "10") q.Add("offset", "0") req.URL.RawQuery = q.Encode()
// Execute the request resp, err := client.Do(req) if err != nil { fmt.Printf("Error executing request: %v", err) return } defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body) fmt.Printf("Response: %s", string(body))}Working with POST Requests and JSON
POST requests with JSON body are a standard task when working with REST APIs. Go provides convenient tools for data serialization.
Sending JSON via POST
package main
import ( "bytes" "encoding/json" "fmt" "net/http" "time")
type User struct { Name string `json:"name"` Email string `json:"email"` Age int `json:"age"`}
func main() { user := User{ Name: "Ivan Petrov", Email: "ivan@example.com", Age: 30, }
// Serialize the struct to JSON jsonData, err := json.Marshal(user) if err != nil { fmt.Printf("JSON serialization error: %v", err) return }
client := &http.Client{Timeout: 5 * time.Second}