A simple, type-safe Go library for loading configuration from environment variables using struct tags.
- π·οΈ Struct-based configuration - Define config with Go structs and tags
- β
Built-in validation -
min,max, andpatterntags plus custom type validators (docs | example) - π― Type-safe - Automatic conversion for primitives, durations, and JSON with generic type handlers
- π§± Building block architecture - Compose custom types from simple, reusable components (docs)
- π Flexible defaults - Struct tags or pre-initialized values (docs)
- π³ Nested structs - Organize configuration hierarchically
- π§ Extensible - Custom types and key stores (docs)
- π¬ Clear errors - Descriptive validation and missing field errors
go get github.com/m0rjc/goconfigpackage main
import (
"context"
"fmt"
"log"
"time"
"github.com/m0rjc/goconfig"
)
type Config struct {
// Basic fields
APIKey string `key:"API_KEY" required:"true"`
Host string `key:"HOST" default:"localhost"`
// With validation
Port int `key:"PORT" default:"8080" min:"1024" max:"65535"`
Timeout time.Duration `key:"TIMEOUT" default:"30s" min:"1s" max:"5m"`
// Nested configuration
Database struct {
Host string `key:"DB_HOST" default:"localhost"`
Port int `key:"DB_PORT" default:"5432"`
Username string `key:"DB_USER" required:"true"`
Password string `key:"DB_PASSWORD" required:"true"`
}
}
func main() {
var config Config
if err := goconfig.Load(context.Background(), &config); err != nil {
log.Fatalf("Failed to load configuration: %v", err)
}
fmt.Printf("Server: %s:%d\n", config.Host, config.Port)
fmt.Printf("Database: %s:%d\n", config.Database.Host, config.Database.Port)
}Set environment variables and run:
export API_KEY="sk-your-api-key"
export DB_USER="appuser"
export DB_PASSWORD="secret"
export PORT="8080"
go run main.go| Tag | Purpose | Example |
|---|---|---|
key |
Environment variable name (required) | key:"PORT" |
default |
Default value if not set | default:"8080" |
min |
Minimum value (numbers, durations) | min:"1024" |
max |
Maximum value (numbers, durations) | max:"65535" |
pattern |
Regex pattern for strings | pattern:"^[a-z]+$" |
scheme |
Command separated list of schemes for *url.URL |
scheme:"http,https" |
required |
Must be present and non-empty | required:"true" |
keyRequired |
Must be present (can be empty) | keyRequired:"true" |
- Primitives:
string,bool - Integers:
int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64 - Floats:
float32,float64 - Duration:
time.Duration- uses Go format: "30s", "5m", "1h" - JSON:
map[string]interface{}or structs withjsontags - Pointers: All above types as pointers
- Nested structs: Organize configuration hierarchically
type ServerConfig struct {
Port int `key:"PORT" default:"8080" min:"1024" max:"65535"`
MaxConns int `key:"MAX_CONNS" default:"100" min:"1" max:"10000"`
LoadFactor float64 `key:"LOAD_FACTOR" default:"0.75" min:"0.0" max:"1.0"`
Timeout time.Duration `key:"TIMEOUT" default:"30s" min:"1s" max:"5m"`
Username string `key:"USERNAME" pattern:"^[a-zA-Z0-9_]+$"`
}Validation errors provide clear messages:
invalid value for PORT: below minimum 1024
Define custom types with validation using the building block architecture - compose simple, reusable components:
type APIKey string
type Config struct {
APIKey APIKey `key:"API_KEY" required:"true"`
}
// Building block approach: parser + validators
apiKeyHandler := goconfig.NewCustomType(
func(rawValue string) (APIKey, error) {
return APIKey(rawValue), nil
},
func(value APIKey) error {
if !strings.HasPrefix(string(value), "sk-") {
return fmt.Errorf("API key must start with 'sk-'")
}
return nil
},
)
err := goconfig.Load(context.Background(), &cfg,
goconfig.WithCustomType[APIKey](apiKeyHandler),
)The building block system lets you compose handlers:
NewCustomType- Start with parser and validatorsAddValidators- Add validators to existing handlersCastCustomType- Transform handlers for type aliasesNewStringEnumType- Specialized enum builder
π Custom Types Guide | Validation Guide | Example
Load complex JSON structures from environment variables:
type ModelParams struct {
Temperature float64 `json:"temperature"`
MaxTokens int `json:"max_tokens"`
}
type Config struct {
Params ModelParams `key:"MODEL_PARAMS"`
}export MODEL_PARAMS='{"temperature":0.7,"max_tokens":1000}'π JSON Guide
If you see an error about parsing JSON when you are not expecting a JSON value, check that the type is recognized.
The JSON handling for struct types catches various types (such as url.URL before I added support for it)
- π Documentation Index - Complete guides and reference
- π§± Custom Types Guide - Building block architecture for custom types
- π Validation - Min/max, pattern, and custom type validators
- βοΈ Defaulting & Required Fields - How defaults and required work
- π JSON Deserialization - Working with JSON config
- π§ Advanced Features - Custom key stores and advanced patterns
- π‘ Examples - Working code examples
- Simple Example - Basic usage with defaults and nested structs
- Validation Example - Comprehensive validation demonstration
Read from sources other than environment variables:
// Composite store: try environment, then fall back to file
store := goconfig.CompositeStore(
goconfig.EnvironmentKeyStore,
fileKeyStore("/etc/myapp/config"),
)
err := goconfig.Load(context.Background(), &cfg,
goconfig.WithKeyStore(store),
)Supports AWS Secrets Manager, HashiCorp Vault, config files, and more.
π Advanced Guide
err := goconfig.Load(context.Background(), &config)
if err != nil {
// Check for specific errors
if errors.Is(err, goconfig.ErrMissingConfigKey) {
log.Fatal("Missing required environment variable")
}
log.Fatalf("Configuration error: %v", err)
}Multiple errors are collected and reported together for easier debugging.
go test -vContributions welcome! Please open an issue or pull request on GitHub.
MIT License - see LICENSE file for details.
- π Documentation
- π GitHub Repository
- π¦ pkg.go.dev