Config Struct
A pattern for grouping related configuration options into a struct for cleaner function signatures.
Contributors
When to Use
- When a function has more than 3-4 related parameters
- When configuration is often passed around together
- When you need clear documentation of all options
When NOT to Use
- For functions with few parameters
- When options are mostly independent
- When extensibility is more important than explicitness (use functional options)
The Config Struct pattern groups related configuration into a struct, making function signatures cleaner and configuration more explicit. It’s simpler than functional options and works well when all configuration is known upfront.
Implementation
package client
import (
"net/http"
"time"
)
type Config struct {
BaseURL string
Timeout time.Duration
MaxRetries int
Headers map[string]string
HTTPClient *http.Client
}
// DefaultConfig returns sensible defaults
func DefaultConfig() Config {
return Config{
Timeout: 30 * time.Second,
MaxRetries: 3,
Headers: make(map[string]string),
HTTPClient: http.DefaultClient,
}
}
type Client struct {
config Config
}
func New(cfg Config) *Client {
// Apply defaults for zero values if needed
if cfg.Timeout == 0 {
cfg.Timeout = 30 * time.Second
}
if cfg.HTTPClient == nil {
cfg.HTTPClient = http.DefaultClient
}
return &Client{config: cfg}
}
func (c *Client) Get(path string) (*http.Response, error) {
// Use config values
req, err := http.NewRequest("GET", c.config.BaseURL+path, nil)
if err != nil {
return nil, err
}
for k, v := range c.config.Headers {
req.Header.Set(k, v)
}
return c.config.HTTPClient.Do(req)
}
Usage
package main
func main() {
// Start with defaults, override what you need
cfg := DefaultConfig()
cfg.BaseURL = "https://api.example.com"
cfg.Timeout = 10 * time.Second
cfg.Headers["Authorization"] = "Bearer token"
client := New(cfg)
// Or construct inline
client := New(Config{
BaseURL: "https://api.example.com",
Timeout: 10 * time.Second,
MaxRetries: 5,
})
resp, err := client.Get("/users")
// ...
}
Benefits
- Self-documenting: All options are visible in the struct definition
- IDE friendly: Autocomplete shows available options
- Easy to validate: All configuration is available in one place
- Testable: Easy to create test configurations
- JSON/YAML friendly: Config structs can be unmarshaled from files