golang-spf13-viper

Golang configuration library using spf13/viper — layered precedence (flag > env > file > KV > default), BindPFlag/BindPFlags, SetEnvPrefix + SetEnvKeyReplacer + AutomaticEnv, ReadInConfig + ConfigFileNotFoundError, Unmarshal + mapstructure struct tags, Sub for sub-trees, WatchConfig + OnConfigChange

By samber · 34,950 installs

npx skills add samber/cc-skills-golang --skill golang-spf13-viper

Source repository · Upstream listing

Persona: You are a Go engineer who treats configuration as a layered system. Flag beats env beats file beats default — and you bind every key so all four layers stay reachable through one API. Using spf13/viper for layered configuration in Go Viper resolves configuration values from multiple sources in a fixed precedence order. It has no user facing surface — it doesn't define commands or flags. Its job is to answer "what is the value of key X right now?" by walking its source layers from highest to lowest priority. Official Resources: [pkg.go.dev/github.com/spf13/viper](https://pkg.go.dev/github.com/spf13/viper) [github.com/spf13/viper](https://github.com/spf13/viper) This skill is not exhaustive — refer to library documentation and code examples for more information: For Go package docs, symbols, versions, importers, and known vulnerabilities, → See samber/cc skills golang@golang pkg go dev skill ( godig ), preferred over Context7 for Go package facts. To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See samber/cc skills golang@golang gopls skill ( gopls ). Context7 remains a fallback for docs not indexed on pkg.go.dev. Viper vs. cobra Cobra owns the command tree — subcommands, flags, arg validation, completions. Viper owns configuration resolution — it answers "what is the value of key X?" by walking its source layers, with no user facing surface of its own: it is purely a key value resolver. Cobra alone — flag only CLIs. Viper alone — config file daemons. Both — bind flags at PersistentPreRunE via BindPFlag . → See samber/cc skills golang@golang spf13 cobra for the cobra side of this integration. The precedence pipeline Viper resolves a key by walking sources in this order (first set value wins): This pipeline is fixed and cannot be reordered. Understanding it prevents most viper bugs: a key that "should" come from a config file may be shadowed by an env var or a flag with a default value. Sources and config files ConfigFileNotFoundError must be handled gracefully — config files are usually optional. An unhandled error from a missing file crashes programs that are perfectly valid when run with only flags or env vars. For supported formats (JSON, TOML, YAML, HCL, INI, properties), MergeInConfig , and remote KV, see [sources and formats.md](references/sources and formats.md). Env binding and key replacers This is the highest bug density area in viper. All three settings must be wired together — missing any one breaks nested key resolution: For BindEnv , AllowEmptyEnv , and env vs default interaction, see [binding and env.md](references/binding and env.md). Flag binding (the cobra seam) Bind cobra flags to viper in init() or PersistentPreRunE — never in RunE (config loading in PersistentPreRunE already ran before RunE , so bindings set in RunE are missed): For AllowEmptyEnv and flag/env interaction details, see [binding and env.md](references/binding and env.md). Unmarshaling into structs viper.Unmarshal maps the resolved configuration into a struct using mapstructure : Always use mapstructure tags — implicit mapping is fragile for nested structs and underscore named fields. Prefer UnmarshalKey("database", &dbCfg) over Sub("database").Unmarshal — it avoids the nil check Sub requires when the key is missing. For time.Duration / net.IP / slice decoders and custom DecodeHook registration, see [unmarshal.md](references/unmarshal.md). Sub trees viper.Sub("database") returns a new viper.Viper scoped to the prefix, or nil if the key does not exist — always nil check before calling methods on the result. Prefer UnmarshalKey("database", &dbCfg) which avoids the nil risk entirely. Hot reload WatchConfig uses fsnotify and watches inodes, so editors that write atomically via rename (vim, neovim) replace the inode and the callback may not fire. Test hot reload with echo config.yaml , not editor saves. For race safe reload patterns, see [watch and reload.md](references/watch and reload.md). Test isolation Never use the global viper in tests — state leaks across test cases. Use viper.New() per test so each instance is isolated: For t.Setenv interactions and Reset() limitations, see [testing and isolation.md](references/testing and isolation.md). Best Practices 1. Set prefix + key replacer + AutomaticEnv together — missing any one causes nested env keys to silently not resolve ( database.host → DATABASE.HOST instead of DATABASE HOST ). 2. Handle ConfigFileNotFoundError gracefully — a missing config file should not crash a service that runs with only flags and env vars. 3. Always use mapstructure tags on config structs — implicit mapping silently misses nested and underscore named fields. 4. Use viper.New() in tests, never the global — the global accumulates state across test runs; per test instances are isolated. 5. Bind flags before Execute() — binding in RunE is too late; cobra parses flags before RunE runs. Common Mistakes Mistake Why it fails Fix AutomaticEnv without SetEnvKeyReplacer database.host looks for MYAPP DATABASE.HOST (dot preserved) — never matches Add SetEnvKeyReplacer(strings.NewReplacer(".", " ")) before AutomaticEnv No mapstructure tags on struct fields Silently misses nested and underscore named fields Add mapstructure:"key name" to every field Using global viper in tests State from one test contaminates the next, causing flaky ordering Create viper.New() per test Missing ConfigFileNotFoundError check Missing config file crashes a service that should run on flags/env alone errors.As(err, &notFound) — only propagate non not found errors Further Reading [sources and formats.md](references/sources and formats.md) — supported file formats, multi path search, MergeInConfig, remote KV (etcd/Consul) [binding and env.md](references/binding and env.md) — BindEnv, AutomaticEnv, SetEnvPrefix, SetEnvKeyReplacer, AllowEmptyEnv, timing rules [unmarshal.md](references/unmarshal.md) — Unmarshal, UnmarshalKey, mapstructure tags, custom DecodeHooks (Duration, IP, slice) [watch and reload.md](references/watch and reload.md) — WatchConfig, OnConfigChange, fsnotify caveats, atomic rename trap, race safe patterns [testing and isolation.md](references/testing and isolation.md) — viper.New() per test, t.Setenv interactions, Reset() limitations, snapshot/restore Cross References → See samber/cc skills golang@golang cli skill for general CLI architecture — project layout, exit codes, signal handling, cobra+viper integration → See samber/cc skills golang@golang spf13 cobra skill for the cobra side of this integration (flag definition and binding) → See samber/cc skills golang@golang testing skill for general Go testing patterns If you encounter a bug or unexpected behavior in spf13/viper, open an issue at <https://github.com/spf13/viper/issues .