HTTP client in Go: making requests correctly from A to Z

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

HTTP client in Go: making requests correctly from A to Z



Go (Golang) is a language renowned for its simplicity and performance, especially in the field of network programming. The built-in net/http package provides a powerful and flexible HTTP client suitable for both simple requests and complex high-load systems. In this article, we will break down how to properly use the HTTP client in Go, starting from basic requests to advanced configurations.



1. Basics: creating and executing simple requests



The simplest way to make an HTTP request in Go is to use helper functions from the net/http package, such as http.Get and http.Post. However, for production code, it is recommended to explicitly create a client and a request.



Basic GET request:



package main

import ( "fmt" "io/ioutil" "net/http")

func main() { // Create a new client client := &http.Client{}

// Create a request req, err := http.NewRequest("GET", "https://api.example.com/data", nil) if err != nil { fmt.Println("Error creating request:", err) return }

// Execute the request resp, err := client.Do(req) if err != nil { fmt.Println("Error executing request:", err) return } defer resp.Body.Close()

// Read the response body body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Error reading response:", err) return }

fmt.Println("Status:", resp.Status) fmt.Println("Body:", string(body))}


POST request with JSON body:



package main

import ( "bytes" "encoding/json" "fmt" "net/http")

type Payload struct { Name string `json:"name"` Email string `json:"email"`}

func main() { payload := Payload{Name: "John", Email: "john@example.com"} jsonData, _ := json.Marshal(payload)

client := &http.Client{} req, err := http.NewRequest("POST", "https://api.example.com/users", bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("Error:", err) return }

// Set headers req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer your-token")

resp, err := client.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close()

fmt.Println("Status:", resp.Status)}


2. Managing timeouts and contexts



One of the most common mistakes is the lack of timeouts. Without them, your client can hang indefinitely waiting for a server response. In Go, there are two main ways to manage timeouts.



Client-level timeout:



client := &http.Client{    Timeout: 10 * time.Second, // Total timeout for the entire request}


Timeout using context (recommended):



ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()

req, err := http.NewRequestWithContext(ctx, "GET", "https://api.example.com/data", nil)if err != nil { log.Fatal(err)}

resp, err := client.Do(req)if err != nil { // Check if the timeout expired if errors.Is(err, context.DeadlineExceeded) { fmt.Println("Request exceeded timeout") } else { fmt.Println("Error:", err) } return}defer resp.Body.Close()


Using a context provides more flexibility: you can cancel the request at any time (for example, when the application closes) and pass values through the context.



3. Error handling and retries



The network is unreliable, so your client should be prepared for temporary failures.

Blogs

Book Recommendations