62 lines
1.6 KiB
Go
62 lines
1.6 KiB
Go
// Package config loads application configuration from a YAML file + env vars.
|
|
// Priority (highest → lowest): env vars → config file → defaults.
|
|
//
|
|
// Usage:
|
|
//
|
|
// cfg, err := config.Load("config/dev.yaml")
|
|
// fmt.Println(cfg.App.Port)
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// Config is the root configuration object. Add sub-structs as the app grows.
|
|
type Config struct {
|
|
App AppConfig `mapstructure:"app"`
|
|
Logger LoggerConfig `mapstructure:"logger"`
|
|
}
|
|
|
|
// AppConfig holds generic application settings.
|
|
type AppConfig struct {
|
|
Name string `mapstructure:"name"`
|
|
Port int `mapstructure:"port"`
|
|
Env string `mapstructure:"env"` // dev | staging | prod
|
|
}
|
|
|
|
// LoggerConfig controls logging behavior.
|
|
type LoggerConfig struct {
|
|
Level string `mapstructure:"level"` // debug | info | warn | error
|
|
}
|
|
|
|
// Load reads the YAML file at path and overlays any matching environment
|
|
// variables (e.g. APP_PORT overrides app.port).
|
|
func Load(path string) (*Config, error) {
|
|
v := viper.New()
|
|
|
|
v.SetConfigFile(path)
|
|
|
|
// Allow every config key to be overridden by an env var.
|
|
// e.g. APP_PORT=9090 overrides app.port
|
|
v.AutomaticEnv()
|
|
|
|
// Sensible defaults so the binary works without a config file.
|
|
v.SetDefault("app.name", "go-template")
|
|
v.SetDefault("app.port", 8080)
|
|
v.SetDefault("app.env", "dev")
|
|
v.SetDefault("logger.level", "info")
|
|
|
|
if err := v.ReadInConfig(); err != nil {
|
|
return nil, fmt.Errorf("config: reading %q: %w", path, err)
|
|
}
|
|
|
|
var cfg Config
|
|
if err := v.Unmarshal(&cfg); err != nil {
|
|
return nil, fmt.Errorf("config: unmarshalling: %w", err)
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|