# README
#+TITLE: go-cfg: Powerful Configuration Management for Go #+PROPERTY: TAGS go configuration library reflection #+PROPERTY: KEY_CONCEPTS Configuration Management, Reflection, Struct Tags, Environment Variables, Command-line Flags #+AUTHOR: go-cfg Project #+OPTIONS: toc:3 num:t
- Table of Contents :TOC:
- [[#overview][Overview]]
- [[#features][Features]]
- [[#quick-start][Quick Start]]
- [[#installation][Installation]]
- [[#basic-usage][Basic Usage]]
- [[#simple-configuration-example][Simple Configuration Example]]
- [[#running-the-example][Running the Example]]
- [[#configuration-sources-priority][Configuration Sources Priority]]
- [[#parsing-modes][Parsing Modes]]
- [[#simple-mode][Simple Mode]]
- [[#flatnest-mode-recommended][FlatNest Mode (Recommended)]]
- [[#flags-mode][Flags Mode]]
- [[#nested-mode][Nested Mode]]
- [[#wrapped-mode][Wrapped Mode]]
- [[#struct-tags-reference][Struct Tags Reference]]
- [[#advanced-features][Advanced Features]]
- [[#complex-type-support][Complex Type Support]]
- [[#configuration-persistence][Configuration Persistence]]
- [[#validation-and-status-checking][Validation and Status Checking]]
- [[#help-text-generation][Help Text Generation]]
- [[#environment-variable-mapping][Environment Variable Mapping]]
- [[#real-world-examples][Real-World Examples]]
- [[#web-server-configuration][Web Server Configuration]]
- [[#microservice-configuration][Microservice Configuration]]
- [[#migration-from-other-libraries][Migration from Other Libraries]]
- [[#from-envconfig][From envconfig]]
- [[#from-flag-package][From flag package]]
- [[#best-practices][Best Practices]]
- [[#1-use-clear-field-names][1. Use Clear Field Names]]
- [[#2-group-related-configuration][2. Group Related Configuration]]
- [[#3-use-appropriate-defaults][3. Use Appropriate Defaults]]
- [[#4-document-complex-configurations][4. Document Complex Configurations]]
- [[#troubleshooting][Troubleshooting]]
- [[#common-issues][Common Issues]]
- [[#debug-mode][Debug Mode]]
- [[#api-reference][API Reference]]
- [[#core-functions][Core Functions]]
- [[#utility-functions][Utility Functions]]
- [[#status-functions][Status Functions]]
- [[#contributing][Contributing]]
- [[#development-setup][Development Setup]]
- [[#running-tests][Running Tests]]
- [[#license][License]]
- [[#copyright-notice][Copyright Notice]]
- Overview
go-cfg is a mature, production-ready configuration management library for Go applications that uses reflection to automatically parse configuration from multiple sources including environment variables, command-line flags, and configuration files.
** Features
- 🚀 Clean Flag Names: FlatNest() generates intuitive flags like --api-host instead of --config-api-host
- 🔄 Multiple Sources: Environment variables, command-line flags, configuration files
- 🏗️ Flexible Modes: FlatNest, Simple, Nested, Wrapped, and Flags parsing modes
- 📝 Type Safety: Full support for Go primitive and complex types (maps, slices, time.Duration)
- 💾 Persistence: Save and load configurations in JSON/YAML format
- 🎯 Validation: Required fields and default values with struct tags
- 📚 Enhanced Help: Environment variable display in help text
- 🌳 Nested Structures: Full support for nested and embedded structs
** Quick Start
#+BEGIN_SRC bash go get github.com/davidwalter0/go-cfg #+END_SRC
- Installation
#+BEGIN_SRC bash go get github.com/davidwalter0/go-cfg #+END_SRC
- Basic Usage
** Simple Configuration Example
#+BEGIN_SRC go :tangle /tmp/go-cfg/main.go :mkdirp true :tangle-mode o0644 :package 'discard :main no package main
import ( "fmt" "log" "time" "github.com/davidwalter0/go-cfg" )
// Define your configuration structure
type Config struct {
// Basic types with defaults
Host string default:"localhost" json:"host"
Port int default:"8080" json:"port"
DiscoveryEndpoints map[string]int default:"consul:8080,etcd:2379,server:1234"
// Required fields
APIKey string `required:"true" json:"api_key"`
// Complex types
Tags []string `default:"web,api,production" json:"tags"`
// Nested configuration
Database struct {
Host string `default:"localhost" json:"host"`
Port int `default:"5432" json:"port"`
Name string `required:"true" json:"name"`
Username string `required:"true" json:"username"`
Password string `required:"true" json:"password"`
} `json:"database"`
// Advanced types
Timeouts map[string]time.Duration `default:"read:30s,write:10s" json:"timeouts"`
}
func main() { var config Config
// Parse configuration using FlatNest for clean flag names
if err := cfg.FlatNest(&config); err != nil {
log.Fatal(err)
}
fmt.Printf("Configuration loaded: %+v\n", config)
} #+END_SRC
#+RESULTS: : Configuration loaded: {Host:localhost Port:8080 DiscoveryEndpoints:map[consul:8080 etcd:2379 server:1234] APIKey: Tags:[web api production web api production] Database:{Host:localhost Port:5432 Name: Username: Password:} Timeouts:map[read:30s write:10s]}
** Running the Example
#+BEGIN_SRC bash :results org
Set environment variables
export API_KEY="your-secret-key" export DATABASE_NAME="myapp" export DATABASE_USERNAME="user" export DATABASE_PASSWORD="pass"
Run with default values
go run /tmp/go-cfg/main.go
Override with command-line flags
go run /tmp/go-cfg/main.go --host=0.0.0.0 --port=9000 --tags=staging,debug go run /tmp/go-cfg/main.go --host=0.0.0.0 --port=9000 --tags=staging,debug --help 2>&1 || true #+END_SRC
#+RESULTS: #+begin_src org Configuration loaded: {Host:localhost Port:8080 DiscoveryEndpoints:map[consul:8080 etcd:2379 server:1234] APIKey:your-secret-key Tags:[web api production] Database:{Host:localhost Port:5432 Name:myapp Username:user Password:pass} Timeouts:map[read:30s write:10s]} Configuration loaded: {Host:0.0.0.0 Port:9000 DiscoveryEndpoints:map[consul:8080 etcd:2379 server:1234] APIKey:your-secret-key Tags:[staging debug] Database:{Host:localhost Port:5432 Name:myapp Username:user Password:pass} Timeouts:map[read:30s write:10s]}
Usage of main: -api-key string Env API_KEY : (api_key) (string) -database-host string Env DATABASE_HOST : (host) (string) (default "localhost") -database-name string Env DATABASE_NAME : (name) (string) -database-password string Env DATABASE_PASSWORD : (password) (string) -database-port int Env DATABASE_PORT : (port) (int) (default 5432) -database-username string Env DATABASE_USERNAME : (username) (string) -discovery-endpoints value Env DISCOVERY_ENDPOINTS : (DiscoveryEndpoints) () (default consul:8080,etcd:2379,server:1234) -host string Env HOST : (host) (string) (default "localhost") -port int Env PORT : (port) (int) (default 8080) -tags value Env TAGS : (tags) () (default web,api,production) -timeouts value Env TIMEOUTS : (timeouts) () (default read:30s,write:10s) exit status 2 #+end_src
- Configuration Sources Priority
go-cfg processes configuration sources in the following order (later sources override earlier ones):
- Default values (from struct tags)
- Environment variables (CamelCase → UPPER_SNAKE_CASE)
- Configuration files (if loaded)
- Command-line flags (CamelCase → kebab-case)
- Parsing Modes
** Simple Mode
No prefixes, direct field mapping:
#+BEGIN_SRC go
type Config struct {
Host string default:"localhost"
Port int default:"8080"
}
var config Config cfg.Simple(&config) // Creates --host and --port flags #+END_SRC
** FlatNest Mode (Recommended)
Clean nested flags without verbose prefixes:
#+BEGIN_SRC go var config Config cfg.FlatNest(&config) // Clean flags: --api-host, --database-port #+END_SRC
** Flags Mode
Standard flag parsing (use only with unique field names):
#+BEGIN_SRC go var config Config cfg.Flags(&config) // Risk of collisions with nested structs #+END_SRC
** Nested Mode
Preserves hierarchical structure:
#+BEGIN_SRC go
type Config struct {
Server struct {
Host string default:"localhost"
Port int default:"8080"
}
}
var config Config cfg.Nest(&config) // Creates --server-host and --server-port #+END_SRC
** Wrapped Mode
Adds prefix to all options:
#+BEGIN_SRC go var config Config cfg.Wrap("myapp", &config) // Creates --myapp-host and --myapp-port #+END_SRC
- Struct Tags Reference
| Tag | Purpose | Example | |-----------+------------------------+------------------------| | default | Set default value | default:"localhost" | | required | Mark field as required | required:"true" | | short | Short flag name | short:"h" | | json | JSON field name | json:"host_name" | | name | Override flag name | name:"hostname" |
- Advanced Features
** Complex Type Support
#+BEGIN_SRC go
type AdvancedConfig struct {
// String slices (comma-delimited)
Tags []string default:"web,api,backend"
// Integer slices
Ports []int `default:"8080,8081,8082"`
// Maps with typed values
Limits map[string]int `default:"cpu:100,memory:512,disk:1024"`
// Duration types
Timeout time.Duration `default:"30s"`
ReadTimeout time.Duration `default:"5m"`
// Nested maps
Features map[string]bool `default:"auth:true,logging:false,metrics:true"`
} #+END_SRC
** Configuration Persistence
#+BEGIN_SRC go // Save configuration to file if err := cfg.Save("config.yaml", &config); err != nil { log.Fatal(err) }
// Load configuration from file if err := cfg.Load("config.yaml", &config); err != nil { log.Fatal(err) }
// Load then parse flags (file values can be overridden by flags) cfg.Load("config.yaml", &config) cfg.Flags(&config) #+END_SRC
** Validation and Status Checking
#+BEGIN_SRC go // Check if a field was set (vs using default) if cfg.IsSet("api-key") { fmt.Println("API key was explicitly provided") }
// Check if required fields are satisfied if !cfg.Ok("database-password") { log.Fatal("Required database password not provided") }
// Check if a field is required if cfg.Required("api-key") { fmt.Println("API key is a required field") } #+END_SRC
** Help Text Generation
go-cfg automatically generates help text based on your struct definitions:
#+BEGIN_SRC go var config Config cfg.Flags(&config) // Run with --help to see auto-generated usage information #+END_SRC
- Environment Variable Mapping
go-cfg automatically maps struct fields to environment variables using UPPER_SNAKE_CASE conversion:
| Struct Field | Environment Variable | Command Flag | |-----------------+----------------------+----------------------| | Host | HOST | --host | | APIKey | API_KEY | --api-key | | DatabaseHost | DATABASE_HOST | --database-host | | MaxRetryCount | MAX_RETRY_COUNT | --max-retry-count |
- Real-World Examples
** Web Server Configuration
#+BEGIN_SRC go
type ServerConfig struct {
// Server settings
Host string default:"0.0.0.0" json:"host"
Port int default:"8080" json:"port"
// TLS configuration
TLSCertFile string `json:"tls_cert_file"`
TLSKeyFile string `json:"tls_key_file"`
// Timeouts
ReadTimeout time.Duration `default:"30s" json:"read_timeout"`
WriteTimeout time.Duration `default:"30s" json:"write_timeout"`
IdleTimeout time.Duration `default:"120s" json:"idle_timeout"`
// Features
EnableMetrics bool `default:"true" json:"enable_metrics"`
EnableLogging bool `default:"true" json:"enable_logging"`
LogLevel string `default:"info" json:"log_level"`
// Database
DatabaseURL string `required:"true" json:"database_url"`
MaxDBConns int `default:"10" json:"max_db_connections"`
} #+END_SRC
** Microservice Configuration
#+BEGIN_SRC go
type MicroserviceConfig struct {
// Service identification
ServiceName string required:"true" json:"service_name"
Version string default:"unknown" json:"version"
Environment string default:"development" json:"environment"
// API configuration
API struct {
Host string `default:"0.0.0.0" json:"host"`
Port int `default:"8080" json:"port"`
Timeout time.Duration `default:"30s" json:"timeout"`
RateLimit int `default:"1000" json:"rate_limit"`
} `json:"api"`
// External services
Services map[string]string `json:"services"`
// Feature flags
Features map[string]bool `default:"auth:true,metrics:true,tracing:false" json:"features"`
// Observability
Metrics struct {
Enabled bool `default:"true" json:"enabled"`
Endpoint string `default:"/metrics" json:"endpoint"`
Port int `default:"9090" json:"port"`
} `json:"metrics"`
} #+END_SRC
- Migration from Other Libraries
** From envconfig
#+BEGIN_SRC go
// Before (envconfig)
type Config struct {
Host string envconfig:"HOST" default:"localhost"
Port int envconfig:"PORT" default:"8080"
}
// After (go-cfg)
type Config struct {
Host string default:"localhost"
// AUTO: HOST env var
Port int default:"8080"
// AUTO: PORT env var
}
#+END_SRC
** From flag package
#+BEGIN_SRC go // Before (standard flag package) var host = flag.String("host", "localhost", "Server host") var port = flag.Int("port", 8080, "Server port")
// After (go-cfg)
type Config struct {
Host string default:"localhost"
// AUTO: --host flag
Port int default:"8080"
// AUTO: --port flag
}
var config Config
cfg.Flags(&config)
#+END_SRC
- Best Practices
** 1. Use Clear Field Names
#+BEGIN_SRC go
// Good
type Config struct {
DatabaseHost string default:"localhost"
APIKey string required:"true"
}
// Avoid type Config struct { Host string // Ambiguous: which host? Key string // Unclear purpose } #+END_SRC
** 2. Group Related Configuration
#+BEGIN_SRC go
type Config struct {
Database struct {
Host string default:"localhost"
Port int default:"5432"
Username string required:"true"
Password string required:"true"
}
Redis struct {
Host string `default:"localhost"`
Port int `default:"6379"`
Password string
}
} #+END_SRC
** 3. Use Appropriate Defaults
#+BEGIN_SRC go
type Config struct {
// Production-safe defaults
Host string default:"127.0.0.1"
// Not 0.0.0.0
Timeout time.Duration default:"30s"
// Reasonable timeout
MaxRetries int default:"3"
// Reasonable retry count
EnableDebug bool default:"false"
// Safe for production
}
#+END_SRC
** 4. Document Complex Configurations
#+BEGIN_SRC go
type Config struct {
// Service discovery endpoints (comma-separated)
// Example: "consul:8500,etcd:2379"
DiscoveryEndpoints []string json:"discovery_endpoints"
// Feature flags with service:enabled format
// Example: "auth:true,metrics:false,tracing:true"
Features map[string]bool `default:"auth:true" json:"features"`
} #+END_SRC
- Troubleshooting
** Common Issues
*** Required field not recognized
#+BEGIN_EXAMPLE Error: required field 'api-key' not set #+END_EXAMPLE
Solution: Check environment variable naming (API_KEY) or use explicit flag (--api-key)
*** Type conversion error
#+BEGIN_EXAMPLE Error: parsing "invalid" as int: invalid syntax #+END_EXAMPLE
Solution: Ensure values match the expected type or provide valid defaults
*** Duplicate flag names
#+BEGIN_EXAMPLE Error: flag redefined: host #+END_EXAMPLE
Solution: Use different struct field names or name tags to avoid conflicts
** Debug Mode
Enable debug output to see how values are being processed:
#+BEGIN_SRC go // Enable debug mode before parsing cfg.SetDebug(true) cfg.Flags(&config) #+END_SRC
- API Reference
** Core Functions
*** cfg.FlatNest(ptrs ...interface{}) error
Clean nested flag parsing (recommended for most use cases with nested structures).
*** cfg.Flags(ptrs ...interface{}) error
Standard flag parsing (use only with flat structures or unique field names).
*** cfg.Simple(ptrs ...interface{}) error
Simple parsing without prefixes or nesting.
*** cfg.Nest(ptrs ...interface{}) error
Nested parsing that preserves struct hierarchy in flag names.
*** cfg.Wrap(prefix string, ptrs ...interface{}) error
Wrapped parsing that adds a prefix to all flags.
*** cfg.Eval(ptrs ...interface{}) error
Runtime evaluation without flag parsing.
** Utility Functions
*** cfg.IsSet(flagName string) bool
Returns true if the flag was explicitly set (not using default value).
*** cfg.Ok(flagName string) bool
Returns true if required field validation passes.
*** cfg.Required(flagName string) bool
Returns true if the field is marked as required.
*** cfg.Save(filename string, config interface{}) error
Save configuration to JSON/YAML file.
*** cfg.Load(filename string, config interface{}) error
Load configuration from JSON/YAML file.
** Status Functions
*** cfg.SetDebug(enable bool)
Enable or disable debug output during parsing.
*** cfg.Reset()
Reset internal state for multiple parsing operations.
- Contributing
We welcome contributions! Please ensure:
- All code follows Go formatting standards (gofmt)
- Add tests for new functionality
- Update documentation for API changes
- Follow existing patterns and conventions
** Development Setup
#+BEGIN_SRC bash git clone https://github.com/davidwalter0/go-cfg cd go-cfg go mod tidy go test ./... #+END_SRC
** Running Tests
#+BEGIN_SRC bash
Run all tests
go test ./...
Run tests with coverage
go test -cover ./...
Run specific test
go test -run TestFlags ./... #+END_SRC
- License
This project is licensed under the MIT License - see the [[file:LICENSE][LICENSE]] file for details.
** Copyright Notice
Copyright (c) 2020 David Walter
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.