configx
configx
configx
configx is a hierarchical configuration loader built on koanf and validator.
Roadmap
- Module roadmap: configx roadmap
- Global roadmap: ArcGo roadmap
Supported Features
.envloading (WithDotenv)- Configuration file loading (
WithFiles) - Environment variable loading (
WithEnvPrefix) - Custom source priority (
WithPriority) - Set defaults via map or typed object (
WithDefaults,WithDefaultsTyped,WithTypedDefaults) - Optional validation (
WithValidateLevel,WithValidator) - Optional observability (
WithObservability) - Generic and non-generic loading entry points
Loading Flow
configx merges sources by priority. Later sources override earlier ones.
Default priority:
- dotenv
- files
- environment variables
Quick Start
type AppConfig struct {
Name string `validate:"required"`
Port int `validate:"required,min=1,max=65535"`
}
cfg, err := configx.LoadTErr[AppConfig](
configx.WithDotenv(),
configx.WithFiles("config.yaml"),
configx.WithEnvPrefix("APP"),
configx.WithValidateLevel(configx.ValidateLevelStruct),
)
if err != nil {
panic(err)
}Common Scenarios
1) Local Development (.env First)
err := configx.Load(&cfg,
configx.WithDotenv(".env", ".env.local"),
configx.WithIgnoreDotenvError(true),
)2) File + Environment Variable Override
err := configx.Load(&cfg,
configx.WithFiles("config.yaml"),
configx.WithEnvPrefix("APP"),
configx.WithPriority(configx.SourceFile, configx.SourceEnv),
)3) Bootstrap with Defaults Only
err := configx.Load(&cfg,
configx.WithDefaults(map[string]any{
"name": "my-service",
"port": 8080,
}),
)4) Generic Loading API (Recommended)
result := configx.LoadT[AppConfig](
configx.WithFiles("config.yaml"),
)
if result.IsError() {
panic(result.Error())
}
cfg := result.MustGet()5) Explicit Config Object Usage (Dynamic Paths)
c, err := configx.LoadConfig(
configx.WithFiles("config.yaml"),
)
if err != nil {
panic(err)
}
name := c.GetString("app.name")
port := c.GetInt("app.port")
exists := c.Exists("app.debug")
all := c.All()
_, _, _, _ = name, port, exists, all6) Optional Observability (OTel + Prometheus)
otelObs := otelobs.New()
promObs := promobs.New()
obs := observabilityx.Multi(otelObs, promObs)
err := configx.Load(&cfg,
configx.WithObservability(obs),
configx.WithFiles("config.yaml"),
)Validation Modes
ValidateLevelNone: No validationValidateLevelStruct: Run struct validation
If you need custom validators/tags:
v := validator.New(validator.WithRequiredStructEnabled())
err := configx.Load(&cfg,
configx.WithValidator(v),
configx.WithValidateLevel(configx.ValidateLevelStruct),
)Environment Variable Key Mapping
With WithEnvPrefix("APP"):
APP_DATABASE_HOST->database.hostAPP_SERVER_READ_TIMEOUT->server.read.timeout
Production Tips
- Keep source priority explicit in production builds.
- Use defaults for non-critical values to reduce startup failures.
- Use validation for critical fields (ports, credentials, hostnames).
- Keep
.envoptional in production unless explicitly required.
Testing Tips
- Use
WithDefaultsin tests for determinism. - Avoid real env dependencies in unit tests unless testing isolation of
os.Environ. - Use
LoadT[T]in tests to reduce boilerplate.
FAQ
Which source should have highest priority?
In most services, environment variables should be highest priority in production. Common order is: defaults -> file -> env.
Should I use LoadT[T] or LoadConfig?
- Use
LoadT[T]/LoadTErr[T]for strong typing and validation-first workflows. - Use
LoadConfigonly when you need dynamic getters (GetString,Exists,All) or path-based access.
Troubleshooting
Environment variable values not taking effect
Check these first:
WithEnvPrefixmatches actual env key prefix.WithPriorityplacesSourceEnvafter other sources.- Env keys map to dot-path format (
APP_DB_HOST->db.host).
Validation not running
Validation is disabled by default.
Set WithValidateLevel(...), or wire WithValidator(...) plus validation level.
.env file missing causes startup crash
Use WithIgnoreDotenvError(true) in environments where .env is optional.
Defaults shape mismatch
Prefer explicit map defaults for dynamic keys.
For strong typing, keep defaults in the target config type and load through LoadT[T]/LoadTErr[T].
Anti-Patterns
- Relying on implicit source priority in production.
- Reading config directly from process env in business code after adopting
configx. - Disabling validation for critical fields (ports, credentials, URLs).
- Mixing unrelated prefixes for multiple services in shared environments.
Examples
- observability: Load configuration with optional OTel + Prometheus instrumentation.