Use the standard net/http package's http.Get() function for simple requests or http.Client with http.NewRequest() when you need to customize headers, timeouts, or handle errors more granularly. For production code, always create a dedicated http.Client instance with a configured timeout to prevent hanging connections.
Here is the simplest approach using the convenience function:
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
// Simple GET request
resp, err := http.Get("https://api.github.com")
if err != nil {
panic(err)
}
defer resp.Body.Close()
// Check status code before reading
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("unexpected status: %s", resp.Status))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
For production scenarios, you should define a client with a timeout and handle the request manually to ensure robustness:
package main
import (
"fmt"
"io"
"net/http"
"time"
)
func main() {
// Create a client with a timeout to avoid hanging
client := &http.Client{
Timeout: 10 * time.Second,
}
req, err := http.NewRequest("GET", "https://api.github.com", nil)
if err != nil {
panic(err)
}
// Add custom headers if needed
req.Header.Set("User-Agent", "MyGoApp/1.0")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
panic(fmt.Sprintf("request failed with status: %s", resp.Status))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(body))
}
Key takeaways: always check resp.StatusCode before processing the body, ensure you defer resp.Body.Close() to prevent resource leaks, and prefer http.Client over http.Get() in production to enforce timeouts and reuse connections efficiently.