Documentation
¶
Overview ¶
Package config provides struct-centric configuration loading with deterministic merge order. A single-pass Load merges files, the .env file, and environment variables into one nested map, exposed through the high-level Credo API focused on type safety and developer ergonomics.
Source Precedence ¶
Configuration merges in this order (later overrides earlier):
- Base config files — all found (config.json, config.yaml, config.yml) merged
- Env-specific files — when CREDO_ENV is set (via process env or the .env file). Discovery mode: config.{env}.*. Explicit mode (WithFiles): name.{env}.ext.
- .env file — resolved via WithDotenvPath, CREDO_ENV_FILE, or default ".env"; read and parsed exactly once per Load
- Process environment variables — CREDO_* prefix (configurable)
Quick Start ¶
type AppConfig struct {
Debug bool // auto-mapped to key "debug"
Server ServerConfig `credo:"server"` // decodes the nested "server" sub-tree
}
type ServerConfig struct {
Port int // auto-mapped to key "port"
}
c, err := config.Load()
if err != nil {
log.Fatal(err)
}
var cfg AppConfig
if err := c.Unmarshal("", &cfg); err != nil {
log.Fatal(err)
}
Sub-Tree Access ¶
Read a typed section in one call with the generic Config.Get method, or decode into an existing value with Config.Unmarshal:
serverCfg, err := c.Get[ServerConfig]("server")
var serverCfg ServerConfig
c.Unmarshal("server", &serverCfg)
Custom Prefix ¶
Use WithPrefix to override the default "CREDO_" env var prefix:
c, err := config.Load(config.WithPrefix("MYAPP_"))
Adapted From ¶
Map-merge utilities adapted from koanf (MIT). See NOTICES file for full attribution.
Maturity: experimental
Index ¶
Constants ¶
const ( FormatJSON = "json" FormatYAML = "yaml" )
Format constants for use with LoadBytes.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// contains filtered or unexported fields
}
Config holds configuration loaded from files, .env, and environment variables, merged into a single nested map. Create with Load or LoadBytes, then use Config.Unmarshal to extract typed values. Pass an empty key to decode the entire configuration tree.
func Load ¶
Load creates a Config instance and loads all configuration sources. Sources are merged in precedence order (lowest to highest):
- Base config files — all found among candidates, merged in order
- Env-specific files — config.{CREDO_ENV}.* when CREDO_ENV is set
- .env file (CREDO_ENV_FILE or default ".env") — all entries loaded, no prefix filtering
- Process environment variables (prefix-filtered, default "CREDO_")
The .env file is read and parsed once: CREDO_ENV is taken from it before file loading (the process environment wins), and its entries are merged into the config tree after file loading.
Load is NOT concurrency-safe; call it once at startup.
The returned *Config satisfies RawConfig; use Config.Get for typed snapshot access or pass it to credo.WithRawConfig as-is.
func LoadBytes ¶
LoadBytes creates a Config from raw bytes. After parsing, .env and environment variable layers are applied on top (same as Load). This is useful with go:embed to bundle config files in the binary.
//go:embed config.json var configData []byte rc, err := config.LoadBytes(configData, config.FormatJSON)
The returned *Config satisfies RawConfig; use Config.Get for typed snapshot access or pass it to credo.WithRawConfig as-is.
func (*Config) Exists ¶
Exists reports whether the given key path exists in the merged configuration. Dots in the key always act as path separators.
func (*Config) Get ¶
Get decodes the value or sub-tree at the given dotted key path into a value of type T and returns it. It is the typed-snapshot form of Config.Unmarshal:
db, err := cfg.Get[DatabaseConfig]("database")
port, err := cfg.Get[int]("server.port")
T may be a struct, map, slice, or primitive. The same rules as Config.Unmarshal apply: a missing key or decode failure returns an error, type coercion uses WeaklyTypedInput, and a T implementing Validate() error is validated after decoding. On error the zero value of T is returned.
Get is a bootstrap/composition-root helper. Prefer extracting typed config here and injecting it into services via DI over reading string keys inside business code.
func (*Config) MustGet ¶
MustGet is like Config.Get but panics on error. It suits bootstrap and composition-root code where a missing or invalid required key should abort startup.
func (*Config) Unmarshal ¶
Unmarshal decodes the value or sub-tree at the given dotted key path into dst. dst must be a pointer to a struct, map, slice, or primitive (e.g., *int, *string, *bool, *float64, *time.Duration).
Pass an empty key ("") to decode the entire configuration tree into dst. Dots in the key always act as path separators.
Type coercion uses mapstructure's WeaklyTypedInput, which handles common conversions like string to int, string to bool, int to float64, etc. This is particularly useful when env vars (always strings) override typed YAML/JSON values.
If dst implements Validate() error, validation is called automatically after a successful decode.
A present but null value (YAML/JSON null) is a no-op: dst is left unchanged and no error is returned. A null overlays nothing — mirroring how a partial section overlays only the keys it contains — so it neither zeroes dst nor is misreported as missing (Exists still reports it as present). A null never resets dst: to clear a value, set it explicitly in the config or decode into a fresh zero-value target.
Returns an error if the key does not exist or decoding fails.
type Option ¶
type Option func(*options)
Option configures the loading behavior of a Config instance.
func WithDotenvOptional ¶
func WithDotenvOptional() Option
WithDotenvOptional makes a missing explicit .env file a warning instead of an error. This only affects explicit paths set via WithDotenvPath or the CREDO_ENV_FILE environment variable. The default implicit ".env" is always optional regardless of this setting.
func WithDotenvPath ¶
WithDotenvPath overrides the .env file path. Takes precedence over the CREDO_ENV_FILE environment variable. Useful for binary-relative deployments where the working directory differs from the project root.
By default, an explicit path must exist: if the file is missing, Load returns an error. To make a missing file non-fatal, combine with WithDotenvOptional.
func WithFiles ¶
WithFiles overrides the default config file discovery list. All found files are loaded and merged in order (later files override earlier ones for overlapping keys).
Unlike the default discovery list, explicitly specified files are required: if none of the listed files exist, Load returns an error.
When CREDO_ENV is set (via process env or .env file), env-specific files are derived from each listed file by inserting ".{env}" before the extension (e.g., "myapp.yaml" becomes "myapp.production.yaml"). Derived files are optional — missing derived files are silently skipped.
Pass an empty list to explicitly disable file loading.
func WithLogger ¶
WithLogger sets the logger used for load-time warnings (such as a missing optional .env file). Defaults to slog.Default.
func WithPrefix ¶
WithPrefix overrides the default environment variable prefix ("CREDO_").
type RawConfig ¶
type RawConfig interface {
// Unmarshal decodes the value or sub-tree at the given dotted key path
// into dst. dst must be a pointer (to a struct, map, slice, or primitive).
// Returns an error if the key does not exist or decoding fails.
Unmarshal(key string, dst any) error
// Exists reports whether the given key path exists in the merged configuration.
Exists(key string) bool
}
RawConfig provides low-level access to the merged configuration. This is a bootstrap mechanism — application code should use typed config structs injected via DI instead of calling RawConfig directly.
Unmarshal decodes both struct sections and primitive values:
var port int
rawCfg.Unmarshal("server.port", &port)
var dbCfg DatabaseConfig
rawCfg.Unmarshal("databases.default", &dbCfg)