Configuration

Manage application configuration with Forge's flexible configuration system

Configuration

Forge provides a flexible configuration management system that supports multiple configuration sources, environment-specific settings, and dynamic configuration updates.

Configuration Manager Interface

The configuration manager provides a unified interface for accessing configuration values:

type ConfigManager interface {
    // String values
    GetString(key string, defaultValue ...string) string
    
    // Numeric values
    GetInt(key string, defaultValue ...int) int
    GetInt64(key string, defaultValue ...int64) int64
    GetFloat64(key string, defaultValue ...float64) float64
    
    // Boolean values
    GetBool(key string, defaultValue ...bool) bool
    
    // Complex types
    Unmarshal(key string, dest interface{}) error
    
    // Configuration sources
    AddSource(source ConfigSource) error
    
    // Configuration management
    Set(key string, value interface{}) error
    SetDefault(key string, value interface{}) error
}

Configuration Sources

File Sources

YAML Files

config := forge.NewConfigManager()
config.AddSource(forge.NewFileSource("config.yaml"))
config.AddSource(forge.NewFileSource("config.yml"))

JSON Files

config := forge.NewConfigManager()
config.AddSource(forge.NewFileSource("config.json"))

TOML Files

config := forge.NewConfigManager()
config.AddSource(forge.NewFileSource("config.toml"))

Example YAML Configuration:

# config.yaml
app:
  name: "my-app"
  version: "1.0.0"
  environment: "development"

server:
  address: ":8080"
  timeout: "30s"

database:
  host: "localhost"
  port: 5432
  name: "myapp"
  user: "user"
  password: "password"

logging:
  level: "info"
  format: "json"

metrics:
  enabled: true
  path: "/_/metrics"

Environment Variables

Basic Environment Variables

config := forge.NewConfigManager()
config.AddSource(forge.NewEnvSource())

Environment Variable Mapping

// Environment variables are mapped to configuration keys
// APP_NAME -> app.name
// SERVER_ADDRESS -> server.address
// DATABASE_HOST -> database.host

export APP_NAME="my-app"
export SERVER_ADDRESS=":8080"
export DATABASE_HOST="localhost"

Custom Environment Variable Prefix

config.AddSource(forge.NewEnvSource(forge.WithPrefix("MYAPP_")))

// MYAPP_NAME -> app.name
// MYAPP_SERVER_ADDRESS -> server.address

Environment Variable Override

// Environment variables override file configuration
// config.yaml: server.address: ":8080"
// SERVER_ADDRESS=":3000" -> server.address: ":3000"

Command Line Arguments

Flag-based Configuration

config := forge.NewConfigManager()
config.AddSource(forge.NewFlagSource())

Command Line Usage

# Set configuration values via command line
./my-app --server.address=:8080 --database.host=localhost

# Set boolean values
./my-app --metrics.enabled=true --debug=true

# Set numeric values
./my-app --server.timeout=30 --database.port=5432

Custom Flag Names

config.AddSource(forge.NewFlagSource(forge.WithFlagMapping(map[string]string{
    "port": "server.address",
    "db-host": "database.host",
    "db-port": "database.port",
})))

Remote Sources

Consul Integration

config := forge.NewConfigManager()
config.AddSource(forge.NewConsulSource(forge.ConsulConfig{
    Address: "localhost:8500",
    Key:     "my-app/config",
}))

etcd Integration

config := forge.NewConfigManager()
config.AddSource(forge.NewEtcdSource(forge.EtcdConfig{
    Endpoints: []string{"localhost:2379"},
    Key:       "/my-app/config",
}))

Remote Configuration Updates

// Remote sources support dynamic updates
config.AddSource(forge.NewRemoteSource(forge.RemoteConfig{
    URL:     "https://config.example.com/my-app",
    PollInterval: 30 * time.Second,
}))

Configuration Access

Basic Value Access

// String values
appName := config.GetString("app.name", "default-app")
version := config.GetString("app.version", "1.0.0")

// Numeric values
port := config.GetInt("server.port", 8080)
timeout := config.GetInt("server.timeout", 30)

// Boolean values
debug := config.GetBool("debug", false)
metricsEnabled := config.GetBool("metrics.enabled", true)

// Float values
rate := config.GetFloat64("rate.limit", 100.0)

Complex Type Access

// Unmarshal into struct
type DatabaseConfig struct {
    Host     string `yaml:"host"`
    Port     int    `yaml:"port"`
    Name     string `yaml:"name"`
    User     string `yaml:"user"`
    Password string `yaml:"password"`
}

var dbConfig DatabaseConfig
config.Unmarshal("database", &dbConfig)

Configuration Validation

// Validate configuration
func validateConfig(config ConfigManager) error {
    // Required fields
    if config.GetString("app.name") == "" {
        return errors.New("app.name is required")
    }
    
    if config.GetString("database.host") == "" {
        return errors.New("database.host is required")
    }
    
    // Value ranges
    port := config.GetInt("server.port")
    if port < 1 || port > 65535 {
        return errors.New("server.port must be between 1 and 65535")
    }
    
    return nil
}

Environment-Specific Configuration

Configuration Files by Environment

config/
├── base.yaml          # Base configuration
├── development.yaml   # Development overrides
├── staging.yaml       # Staging overrides
├── production.yaml    # Production overrides
└── testing.yaml       # Testing overrides

Base Configuration

# config/base.yaml
app:
  name: "my-app"
  version: "1.0.0"

server:
  address: ":8080"
  timeout: "30s"

database:
  host: "localhost"
  port: 5432
  name: "myapp"

logging:
  level: "info"
  format: "json"

Environment-Specific Overrides

# config/development.yaml
app:
  environment: "development"

database:
  host: "localhost"
  name: "myapp_dev"

logging:
  level: "debug"
  format: "console"

debug:
  enabled: true
# config/production.yaml
app:
  environment: "production"

database:
  host: "${DB_HOST}"
  name: "${DB_NAME}"
  user: "${DB_USER}"
  password: "${DB_PASSWORD}"

logging:
  level: "warn"
  format: "json"

metrics:
  enabled: true
  path: "/_/metrics"

Loading Environment-Specific Configuration

func loadConfig(environment string) ConfigManager {
    config := forge.NewConfigManager()
    
    // Load base configuration
    config.AddSource(forge.NewFileSource("config/base.yaml"))
    
    // Load environment-specific configuration
    envConfig := fmt.Sprintf("config/%s.yaml", environment)
    config.AddSource(forge.NewFileSource(envConfig))
    
    // Load environment variables
    config.AddSource(forge.NewEnvSource())
    
    return config
}

Dynamic Configuration

Configuration Updates

// Watch for configuration changes
config.Watch(func(key string, value interface{}) {
    log.Printf("Configuration changed: %s = %v", key, value)
    
    // Handle specific configuration changes
    switch key {
    case "server.address":
        // Restart server with new address
        restartServer(value.(string))
    case "logging.level":
        // Update logging level
        updateLogLevel(value.(string))
    }
})

Hot Reload

// Enable hot reload for file sources
config.AddSource(forge.NewFileSource("config.yaml", forge.WithHotReload(true)))

// Handle configuration reload
config.OnReload(func() {
    log.Println("Configuration reloaded")
    
    // Update application configuration
    updateApplicationConfig(config)
})

Configuration Security

Secrets Management

// Use environment variables for secrets
config.AddSource(forge.NewEnvSource())

// Use external secrets management
config.AddSource(forge.NewSecretsSource(forge.SecretsConfig{
    Provider: "aws-secrets-manager",
    Region:   "us-east-1",
    Secrets:  []string{"database-password", "api-key"},
}))

Configuration Encryption

// Encrypt sensitive configuration values
config.AddSource(forge.NewEncryptedSource(forge.EncryptedConfig{
    Key:      "encryption-key",
    Provider: "file",
    Path:     "config/encrypted.yaml",
}))

Configuration Testing

Test Configuration

func TestWithTestConfig(t *testing.T) {
    config := forge.NewConfigManager()
    
    // Set test configuration
    config.Set("app.name", "test-app")
    config.Set("server.port", 8080)
    config.Set("database.host", "localhost")
    
    // Test configuration access
    assert.Equal(t, "test-app", config.GetString("app.name"))
    assert.Equal(t, 8080, config.GetInt("server.port"))
    assert.Equal(t, "localhost", config.GetString("database.host"))
}

Configuration Validation Testing

func TestConfigValidation(t *testing.T) {
    config := forge.NewConfigManager()
    
    // Test valid configuration
    config.Set("app.name", "test-app")
    config.Set("server.port", 8080)
    
    err := validateConfig(config)
    assert.NoError(t, err)
    
    // Test invalid configuration
    config.Set("app.name", "")
    config.Set("server.port", 0)
    
    err = validateConfig(config)
    assert.Error(t, err)
}

Best Practices

  1. Environment Separation: Use separate configuration files for different environments
  2. Secrets Management: Never commit secrets to version control
  3. Validation: Validate configuration values at startup
  4. Defaults: Provide sensible default values
  5. Documentation: Document all configuration options
  6. Testing: Test configuration loading and validation
  7. Security: Use secure methods for handling secrets
  8. Performance: Cache configuration values when appropriate

For more information about configuration in specific contexts, see the Environment Setup and Deployment guides.

How is this guide?

Last updated on