Requests is inspired by Python's famous requests library, bringing a more elegant and intuitive approach to HTTP in Go. This library simplifies common HTTP tasks while remaining fully compatible with Go's standard net/http library.
- 🔒 Automatic Safe Body Close - No more
resp.Body.Close()concerns - 📦 Zero External Dependencies - Only depends on Go standard library
- 🌊 Streaming Downloads - Efficient handling of large files
- 🔄 Chunked HTTP Requests - Support for streaming uploads
- 🔗 Keep-Alive & Connection Pooling - Automatic connection reuse management
- 🍪 Sessions with Cookie Persistence - Easy session management
- 🔐 Basic & Digest Authentication - Built-in authentication support
- 🎯 Full http.RoundTripper Implementation - Fully compatible with
net/http - 📁 File System Support - Easy file upload and download
- 🔌 Middleware System - Flexible request/response processing chain
- 🖥️ HTTP Server - Built-in routing and middleware support
- 🐛 Debug Tracing - Built-in HTTP request tracing
Simple is better than complex
Beautiful is better than ugly
Explicit is better than implicit
Practical beats purity
go get github.com/golang-io/requestsRequirements: Go 1.22.1 or higher
package main
import (
"context"
"fmt"
"github.com/golang-io/requests"
)
func main() {
// Create a session
sess := requests.New(requests.URL("https://httpbin.org"))
// Send request (Body is automatically closed)
resp, _ := sess.DoRequest(context.Background(),
requests.Path("/get"),
)
// Content is automatically cached
fmt.Println(resp.Content.String())
}Traditional way (using net/http):
resp, err := http.Get("https://api.example.com/users")
if err != nil {
return err
}
defer resp.Body.Close() // Easy to forget!
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
var users []User
json.Unmarshal(body, &users) // Lots of boilerplateWith Requests:
sess := requests.New(requests.URL("https://api.example.com"))
resp, _ := sess.DoRequest(ctx, requests.Path("/users"))
var users []User
resp.JSON(&users) // Clean and elegant!Session is the core concept in Requests, managing configuration, connection pools, and middleware:
sess := requests.New(
requests.URL("https://api.example.com"),
requests.Header("Authorization", "Bearer token123"),
requests.Timeout(30*time.Second),
requests.MaxConns(100),
)
// All requests inherit session configuration
resp1, _ := sess.DoRequest(ctx, requests.Path("/users"))
resp2, _ := sess.DoRequest(ctx, requests.Path("/posts"))Features:
- ✅ Thread-safe (can be used concurrently by multiple goroutines)
- ✅ Connection reuse (automatic connection pool management)
- ✅ Configuration persistence (session-level config applies to all requests)
Requests supports a flexible two-level configuration system:
// Session-level configuration (applies to all requests)
sess := requests.New(
requests.URL("https://api.example.com"),
requests.Timeout(30*time.Second),
)
// Request-level configuration (applies only to this request, can override session config)
resp, _ := sess.DoRequest(ctx,
requests.Path("/long-task"),
requests.Timeout(60*time.Second), // Override session's 30-second timeout
)Requests provides an enhanced *Response type:
resp, _ := sess.DoRequest(ctx)
// Automatically cached content
fmt.Println(resp.Content.String())
// Parse JSON
var data map[string]any
resp.JSON(&data)
// Request statistics
fmt.Printf("Duration: %v\n", resp.Cost)
stat := resp.Stat()Benefits:
- ✅ Automatic safe closing of
resp.Body - ✅ Content automatically cached in
Content - ✅ Support for multiple reads of response content
- ✅ Request timing and statistics tracking
// Simple GET
resp, _ := requests.Get("https://httpbin.org/get")
// GET with query parameters
sess := requests.New(requests.URL("https://api.example.com"))
resp, _ := sess.DoRequest(ctx,
requests.Path("/users"),
requests.Params(map[string]string{
"page": "1",
"size": "20",
}),
)// POST JSON (automatically serialized)
data := map[string]string{
"name": "John",
"email": "john@example.com",
}
resp, _ := sess.DoRequest(ctx,
requests.MethodPost,
requests.Path("/users"),
requests.Body(data), // Automatically serialized to JSON
requests.Header("Content-Type", "application/json"),
)
// POST form data
form := url.Values{}
form.Set("username", "john")
form.Set("password", "secret")
resp, _ := sess.DoRequest(ctx,
requests.MethodPost,
requests.Form(form), // Automatically sets Content-Type
)sess := requests.New(
requests.URL("https://api.example.com"),
// Session-level headers
requests.Header("Accept", "application/json"),
requests.Header("User-Agent", "MyApp/1.0"),
)
// Request-level headers
resp, _ := sess.DoRequest(ctx,
requests.Header("X-Request-ID", "abc-123"),
requests.Headers(map[string]string{
"X-Custom-1": "value1",
"X-Custom-2": "value2",
}),
)// Basic Authentication
sess := requests.New(
requests.URL("https://api.example.com"),
requests.BasicAuth("username", "password"),
)
// Bearer Token
sess := requests.New(
requests.URL("https://api.example.com"),
requests.Header("Authorization", "Bearer token123"),
)// Small file: one-time read
resp, _ := sess.DoRequest(ctx, requests.Path("/file.txt"))
os.WriteFile("downloaded.txt", resp.Content.Bytes(), 0644)
// Large file: streaming download
file, _ := os.Create("large-file.zip")
defer file.Close()
resp, _ := sess.DoRequest(ctx,
requests.Path("/large-file.zip"),
requests.Stream(func(lineNum int64, data []byte) error {
file.Write(data)
return nil
}),
)// Session-level timeout
sess := requests.New(
requests.Timeout(10*time.Second),
)
// Request-level timeout
resp, _ := sess.DoRequest(ctx,
requests.Timeout(30*time.Second), // Override session config
)
// Context-based timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
resp, _ := sess.DoRequest(ctx)// HTTP proxy
sess := requests.New(
requests.Proxy("http://proxy.company.com:8080"),
)
// SOCKS5 proxy
sess := requests.New(
requests.Proxy("socks5://127.0.0.1:1080"),
)// Request ID middleware
requestIDMiddleware := func(next http.RoundTripper) http.RoundTripper {
return requests.RoundTripperFunc(func(req *http.Request) (*http.Response, error) {
req.Header.Set("X-Request-ID", uuid.New().String())
return next.RoundTrip(req)
})
}
sess := requests.New(
requests.URL("https://api.example.com"),
requests.Use(requestIDMiddleware),
)// Create server
mux := requests.NewServeMux(
requests.Addr("0.0.0.0:8080"),
requests.Use(loggingMiddleware), // Global middleware
)
// Register routes
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
})
mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
// Handle request
}, requests.Use(authMiddleware)) // Route-specific middleware
// Start server
requests.ListenAndServe(context.Background(), mux)| Feature | net/http | requests |
|---|---|---|
| Ease of Use | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Auto Body Close | ❌ | ✅ |
| Session Management | Manual | Automatic |
| Connection Pool | Need Configuration | Built-in |
| JSON Support | Manual | Built-in |
| Middleware System | DIY | Built-in |
| Streaming | Manual | Built-in |
| Debug Tracing | External Tools | Built-in |
| Server Support | Basic | Enhanced |
sess := requests.New(
requests.URL("unix:///var/run/docker.sock"),
)
resp, _ := sess.DoRequest(ctx,
requests.URL("http://localhost/version"),
)transport := &http.Transport{
MaxIdleConns: 200,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
}
sess := requests.New(
requests.RoundTripper(transport),
)sess := requests.New(
requests.URL("https://httpbin.org"),
requests.Trace(), // Enable detailed tracing
)
resp, _ := sess.DoRequest(ctx)
// Output shows: DNS resolution, connection establishment, TLS handshake, request/response detailsresp, _ := sess.DoRequest(ctx)
// Get detailed statistics
stat := resp.Stat()
fmt.Printf("Request duration: %dms\n", stat.Cost)
fmt.Printf("Status code: %d\n", stat.Response.StatusCode)
fmt.Printf("Request URL: %s\n", stat.Request.URL)// ✅ Recommended: Create once, reuse many times
var apiClient = requests.New(
requests.URL("https://api.example.com"),
requests.Timeout(30*time.Second),
)
// ❌ Not recommended: Create new session for each request
func badExample() {
sess := requests.New(...) // Waste of resources
sess.DoRequest(...)
}// ✅ Recommended: DoRequest automatically handles Body closing
resp, _ := sess.DoRequest(ctx)
fmt.Println(resp.Content.String()) // Safe
// ❌ Not recommended: Need to manually close Body
resp, _ := sess.Do(ctx)
defer resp.Body.Close() // Easy to forget// ✅ Recommended: Session-level config + request-level override
sess := requests.New(
requests.URL("https://api.example.com"),
requests.Timeout(10*time.Second), // Default 10 seconds
)
// Most requests use default config
resp1, _ := sess.DoRequest(ctx)
// Special requests override config
resp2, _ := sess.DoRequest(ctx,
requests.Timeout(60*time.Second), // Special task needs 60 seconds
)// ✅ Recommended: Use middleware
sess := requests.New(
requests.Use(
requestIDMiddleware, // Add request ID
retryMiddleware, // Auto retry
loggingMiddleware, // Logging
),
)
// ❌ Not recommended: Repeat code in every request
func badExample() {
req.Header.Set("X-Request-ID", ...) // Repeated code
// Manual retry logic
// Manual logging
}// ✅ Recommended: Complete error handling
resp, err := sess.DoRequest(ctx)
if err != nil {
log.Printf("Request failed: %v", err)
return err
}
if resp.StatusCode != http.StatusOK {
log.Printf("HTTP error: %d", resp.StatusCode)
return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
// ❌ Not recommended: Ignore errors
resp, _ := sess.DoRequest(ctx) // Ignoring errorpackage main
import (
"context"
"fmt"
"github.com/golang-io/requests"
"time"
)
type APIClient struct {
sess *requests.Session
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func NewAPIClient(baseURL, token string) *APIClient {
sess := requests.New(
requests.URL(baseURL),
requests.Header("Authorization", "Bearer "+token),
requests.Header("Accept", "application/json"),
requests.Header("Content-Type", "application/json"),
requests.Timeout(30*time.Second),
requests.MaxConns(100),
)
return &APIClient{sess: sess}
}
func (c *APIClient) GetUser(ctx context.Context, userID int) (*User, error) {
resp, err := c.sess.DoRequest(ctx,
requests.Path(fmt.Sprintf("/users/%d", userID)),
)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("API error: %d", resp.StatusCode)
}
var user User
if err := resp.JSON(&user); err != nil {
return nil, err
}
return &user, nil
}
func (c *APIClient) CreateUser(ctx context.Context, user *User) (*User, error) {
resp, err := c.sess.DoRequest(ctx,
requests.MethodPost,
requests.Path("/users"),
requests.Body(user),
)
if err != nil {
return nil, err
}
var created User
resp.JSON(&created)
return &created, nil
}
func main() {
client := NewAPIClient("https://api.example.com", "your-token")
user, _ := client.GetUser(context.Background(), 123)
fmt.Printf("User: %s\n", user.Name)
}| Option | Description | Example |
|---|---|---|
URL(string) |
Set target URL | requests.URL("https://api.example.com") |
Path(string) |
Append path | requests.Path("/users") |
Method(string) |
Set HTTP method | requests.MethodPost |
Timeout(duration) |
Set timeout | requests.Timeout(30*time.Second) |
Header(k, v) |
Add header | requests.Header("Accept", "application/json") |
BasicAuth(user, pass) |
Basic authentication | requests.BasicAuth("admin", "secret") |
Body(any) |
Set request body | requests.Body(map[string]string{"key": "value"}) |
Form(values) |
Form data | requests.Form(url.Values{...}) |
Params(map) |
Query parameters | requests.Params(map[string]string{...}) |
Proxy(addr) |
Set proxy | requests.Proxy("http://proxy:8080") |
MaxConns(int) |
Max connections | requests.MaxConns(100) |
Verify(bool) |
Verify certificate | requests.Verify(false) |
| Option | Description | Example |
|---|---|---|
Use(middleware...) |
Register middleware | requests.Use(loggingMiddleware) |
CertKey(cert, key) |
TLS certificate | requests.CertKey("cert.pem", "key.pem") |
OnStart(func) |
Start callback | requests.OnStart(func(s *http.Server){...}) |
OnShutdown(func) |
Shutdown callback | requests.OnShutdown(func(s *http.Server){...}) |
We welcome all forms of contributions!
- 🐛 Report bugs
- 💡 Suggest new features
- 📖 Improve documentation
- 🔧 Submit pull requests
Please see CONTRIBUTING.md for details.
This project is licensed under the Apache License 2.0.
- Inspired by Python's requests library
- Thanks to all contributors
If this project helps you, please give us a ⭐ Star!
Made with ❤️ by the Requests Team