A lightweight, zero-dependency, and highly extensible configuration management library for Go applications.
- Zero Dependencies - No external dependencies in the core library
- Plugin-Based Architecture - Mix and match only the configuration sources you need
- Type-Safe - Strongly typed configuration with struct tags
- Multiple Sources - Support for defaults, environment variables, command-line flags, and config files
- Nested Structures - Full support for nested configuration structs
- Rich Type Support - All basic Go types,
time.Duration, and custom types viaencoding.TextUnmarshaler - Validation - Built-in validation support through plugins
- Documentation Generation - Auto-generate markdown documentation for your configuration
go get github.com/sxwebdev/xconfigpackage main
import (
"fmt"
"log"
"github.com/sxwebdev/xconfig"
)
type Config struct {
Host string `default:"localhost" env:"HOST" flag:"host" usage:"Server host address"`
Port int `default:"8080" env:"PORT" flag:"port" usage:"Server port"`
Debug bool `default:"false" env:"DEBUG" flag:"debug" usage:"Enable debug mode"`
Database struct {
Host string `default:"localhost" env:"DB_HOST" usage:"Database host"`
Port int `default:"5432" env:"DB_PORT" usage:"Database port"`
Name string `default:"myapp" env:"DB_NAME" usage:"Database name"`
Password string `secret:"DB_PASSWORD" usage:"Database password"`
}
}
func main() {
cfg := &Config{}
// Load configuration from defaults, env vars, and flags
_, err := xconfig.Load(cfg)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Server: %s:%d\n", cfg.Host, cfg.Port)
fmt.Printf("Database: %s:%d/%s\n", cfg.Database.Host, cfg.Database.Port, cfg.Database.Name)
}The Load function provides the most common configuration pattern, automatically enabling:
- Default values from struct tags
- Custom defaults via
SetDefaults()method - Configuration files (if provided)
- Environment variables
- Command-line flags
type AppConfig struct {
APIKey string `default:"dev-key" env:"API_KEY" flag:"api-key"`
Timeout int `default:"30" env:"TIMEOUT" flag:"timeout"`
EnableLog bool `default:"true" env:"ENABLE_LOG" flag:"enable-log"`
}
cfg := &AppConfig{}
_, err := xconfig.Load(cfg)xConfig supports multiple configuration file formats through decoders:
import (
"encoding/json"
"github.com/sxwebdev/xconfig"
"github.com/sxwebdev/xconfig/plugins/loader"
"github.com/sxwebdev/xconfig/decoders/xconfigyaml"
"github.com/sxwebdev/xconfig/decoders/xconfigdotenv"
)
type Config struct {
Server struct {
Host string `json:"host"`
Port int `json:"port"`
} `json:"server"`
}
cfg := &Config{}
// Create loader with JSON decoder
l, err := loader.NewLoader(map[string]loader.Unmarshal{
"json": json.Unmarshal,
"yaml": xconfigyaml.New().Unmarshal,
"env": xconfigdotenv.New().Unmarshal,
})
if err != nil {
log.Fatal(err)
}
// Add configuration file (optional=false means file must exist)
err = l.AddFile("config.json", false)
if err != nil {
log.Fatal(err)
}
_, err = xconfig.Load(cfg, xconfig.WithLoader(l))type Config struct {
APIKey string `env:"API_KEY"`
Secret string `env:"SECRET"`
}
cfg := &Config{}
// All env vars will be prefixed with "MYAPP_"
// So it will look for: MYAPP_API_KEY and MYAPP_SECRET
_, err := xconfig.Load(cfg, xconfig.WithEnvPrefix("MYAPP"))Implement the SetDefaults() method to programmatically set default values:
type Config struct {
Host string
Port int
URLs []string
}
func (c *Config) SetDefaults() {
c.Host = "localhost"
c.Port = 8080
c.URLs = []string{"https://api.example.com", "https://backup.example.com"}
}
cfg := &Config{}
_, err := xconfig.Load(cfg)
// cfg.Host will be "localhost" unless overridden by env or flagsUse the secret tag for sensitive data that should be loaded from a secret provider:
import (
"github.com/sxwebdev/xconfig"
"github.com/sxwebdev/xconfig/plugins/secret"
)
type Config struct {
DBPassword string `secret:"DATABASE_PASSWORD"`
APIToken string `secret:"API_TOKEN"`
}
// Custom secret provider (e.g., AWS Secrets Manager, HashiCorp Vault, etc.)
secretProvider := func(name string) (string, error) {
// Implement your secret fetching logic here
switch name {
case "DATABASE_PASSWORD":
return fetchFromVault(name)
case "API_TOKEN":
return fetchFromAWS(name)
default:
return "", fmt.Errorf("secret not found: %s", name)
}
}
cfg := &Config{}
_, err := xconfig.Load(cfg, xconfig.WithPlugins(secret.New(secretProvider)))Add validation to ensure your configuration meets requirements:
import (
"fmt"
"github.com/sxwebdev/xconfig"
"github.com/sxwebdev/xconfig/plugins/validate"
)
type Config struct {
Port int `default:"8080"`
Host string `default:"localhost"`
}
// Implement Validate method
func (c *Config) Validate() error {
if c.Port < 1 || c.Port > 65535 {
return fmt.Errorf("port must be between 1 and 65535")
}
if c.Host == "" {
return fmt.Errorf("host cannot be empty")
}
return nil
}
cfg := &Config{}
// Validation happens automatically after loading
_, err := xconfig.Load(cfg)
if err != nil {
log.Fatal(err) // Will fail if validation fails
}You can also use external validators:
import (
"github.com/go-playground/validator/v10"
"github.com/sxwebdev/xconfig/plugins/validate"
)
type Config struct {
Email string `validate:"required,email"`
Age int `validate:"gte=0,lte=130"`
}
cfg := &Config{}
v := validator.New()
_, err := xconfig.Load(cfg, xconfig.WithPlugins(
validate.New(func(a any) error {
return v.Struct(a)
}),
))Control which plugins are enabled:
cfg := &Config{}
// Skip certain plugins
_, err := xconfig.Load(cfg,
xconfig.WithSkipDefaults(), // Don't load from 'default' tags
xconfig.WithSkipEnv(), // Don't load from environment
xconfig.WithSkipFlags(), // Don't load from command-line flags
xconfig.WithSkipCustomDefaults(), // Don't call SetDefaults()
xconfig.WithDisallowUnknownFields(), // Fail if config files contain unknown fields
)Unknown Fields Validation: Enable WithDisallowUnknownFields() to detect typos and configuration errors in JSON/YAML files. When enabled, loading will fail if any fields in the config files don't match your struct definition. Use xconfig.GetUnknownFields() to retrieve unknown fields without failing.
Generate markdown documentation for your configuration:
type Config struct {
Host string `default:"localhost" usage:"Server host address"`
Port int `default:"8080" usage:"Server port number"`
APIKey string `secret:"API_KEY" usage:"API authentication key"`
}
cfg := &Config{}
markdown, err := xconfig.GenerateMarkdown(cfg)
if err != nil {
log.Fatal(err)
}
// Save to file
os.WriteFile("CONFIG.md", []byte(markdown), 0644)Get runtime configuration information:
cfg := &Config{}
c, err := xconfig.Load(cfg)
if err != nil {
log.Fatal(err)
}
usage, err := c.Usage()
if err != nil {
log.Fatal(err)
}
fmt.Println(usage)| Tag | Description | Example |
|---|---|---|
default |
Default value for the field | default:"8080" |
env |
Environment variable name | env:"PORT" |
flag |
Command-line flag name | flag:"port" |
secret |
Secret identifier for secret provider | secret:"DB_PASSWORD" |
usage |
Description for documentation/help | usage:"Server port" |
xconfig |
Override field name in flat structure | xconfig:"custom_name" |
| Plugin | Description |
|---|---|
| defaults | Load values from default struct tags |
| customdefaults | Call SetDefaults() method if implemented |
| env | Load values from environment variables |
| flag | Load values from command-line flags |
| loader | Load values from configuration files (JSON, YAML, etc.) |
| secret | Load sensitive values from secret providers |
| validate | Validate configuration after loading |
Create your own plugins by implementing the Plugin interface with either Walker or Visitor:
import (
"github.com/sxwebdev/xconfig/flat"
"github.com/sxwebdev/xconfig/plugins"
)
type myPlugin struct {
fields flat.Fields
}
// Visitor interface - called once with all fields
func (p *myPlugin) Visit(fields flat.Fields) error {
p.fields = fields
// Setup phase: register metadata, validate structure, etc.
return nil
}
// Parse is called to actually load configuration
func (p *myPlugin) Parse() error {
for _, field := range p.fields {
// Load configuration for each field
}
return nil
}
// Use your custom plugin
cfg := &Config{}
_, err := xconfig.Custom(cfg, &myPlugin{})- All basic Go types:
string,bool,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,float32,float64 time.Duration- Slices of supported types:
[]string,[]int, etc. - Any type implementing
encoding.TextUnmarshaler
See the examples directory for more complete examples.
MIT License