Viper — API Surface#

API types#

Library — pure Go library with no HTTP server, no gRPC service, no CLI binary. All exposure is through exported Go symbols (functions, types, interfaces, methods).


Library API#

Public packages#

PackagePurpose
github.com/spf13/viperCore library — the only public-facing package
github.com/spf13/viper/remoteOptional remote k/v store integration (separate module, blank-import self-registration)

There are no pkg/ or cmd/ directories. All internal packages (internal/encoding/*, internal/features, internal/testutil) are hidden from consumers.


API style#

Dual-surface design: every capability is exposed both as a package-level function (targeting a global singleton initialised in init()) and as a method on *Viper (for multi-instance use). Zero logic duplication — global functions are one-liners delegating to the singleton.

// Global (singleton) form
viper.Get("key")
viper.ReadInConfig()

// Instance form
v := viper.New()
v.Get("key")
v.ReadInConfig()

Constructor functions#

FunctionSignatureNotes
New() *ViperAllocates and initialises a fresh Viper with sensible defaults
NewWithOptions(opts ...Option) *ViperCalls New() then applies each Option
GetViper() *ViperReturns the package-level singleton
SetOptions(opts ...Option)Applies options to the singleton after construction
Reset()Resets the singleton to a fresh New() state (testing aid)

Functional options (Option interface)#

Options inject configuration into Viper at construction time via NewWithOptions.

Option constructorEffect
KeyDelimiter(d string)Sets the key-path delimiter (default ".")
EnvKeyReplacer(r StringReplacer)Replaces characters in env key lookups
WithDecodeHook(h mapstructure.DecodeHookFunc)Overrides the default mapstructure decode hook
WithEncoderRegistry(r EncoderRegistry)Injects a custom encoder registry
WithDecoderRegistry(r DecoderRegistry)Injects a custom decoder registry
WithCodecRegistry(r CodecRegistry)Injects a custom codec registry (combines both)
WithLogger(l *slog.Logger)Injects a structured logger
WithFinder(f Finder)Injects a custom config-file finder (experimental feature path)
ExperimentalFinder()Enables the XDG-compliant locafero-based finder (build tag viper_finder)
ExperimentalBindStruct()Enables BindStruct capability (build tag viper_bind_struct)

Source binding API#

These methods attach config sources to the registry. They do not immediately read values; values are resolved lazily when Get* is called.

Config file#

MethodSignatureDescription
SetConfigFile(in string)Pin an explicit config file path
SetConfigName(in string)Set config filename without extension (searched across AddConfigPath dirs)
SetConfigType(in string)Force a format (yaml/json/toml/dotenv/env) regardless of extension
SetConfigPermissions(perm os.FileMode)File permissions for written configs (default 0644)
AddConfigPath(in string)Add a directory to the config search path
SetFs(fs afero.Fs)Replace the underlying filesystem abstraction
ConfigFileUsed() stringReturn the path of the config file that was read

Config read/write#

MethodSignatureDescription
ReadInConfig() errorFind and decode config file into v.config
MergeInConfig() errorMerge a config file on top of existing v.config
ReadConfig(in io.Reader) errorDecode from an arbitrary io.Reader
MergeConfig(in io.Reader) errorMerge from an arbitrary io.Reader
MergeConfigMap(cfg map[string]any) errorMerge from an in-memory map
WriteConfig() errorWrite current config to the file set via SetConfigFile
SafeWriteConfig() errorLike WriteConfig but errors if file already exists
WriteConfigAs(filename string) errorWrite to a specific path, overwriting if present
WriteConfigTo(w io.Writer) errorEncode current config to an io.Writer
SafeWriteConfigAs(filename string) errorLike WriteConfigAs but errors if file already exists

Environment variables#

MethodSignatureDescription
AutomaticEnv()Enable automatic env var lookup for every key (with prefix if set)
SetEnvPrefix(in string)Prefix for automatic env lookups
GetEnvPrefix() stringReturn the current prefix
SetEnvKeyReplacer(r *strings.Replacer)Transform key to env var name (e.g. -_)
AllowEmptyEnv(allowEmptyEnv bool)Treat empty env var values as set (default: ignore empty)
BindEnv(input ...string) errorBind a key to one or more env var names
MustBindEnv(input ...string)Like BindEnv but panics on error

Flags (pflag integration)#

MethodSignatureDescription
BindPFlags(flags *pflag.FlagSet) errorBind all flags in a pflag.FlagSet
BindPFlag(key string, flag *pflag.Flag) errorBind a single pflag.Flag to a key
BindFlagValues(flags FlagValueSet) errorBind via the abstract FlagValueSet interface (pflag-agnostic)
BindFlagValue(key string, flag FlagValue) errorBind a single abstract FlagValue to a key

Remote key/value stores (via blank-imported viper/remote)#

FunctionSignatureDescription
AddRemoteProvider(provider, endpoint, path string) errorRegister a k/v backend (etcd/Consul/NATS/Firestore)
AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) errorRegister an encrypted backend
ReadRemoteConfig() errorFetch and decode config from registered remote providers
WatchRemoteConfig() errorBlock and continuously sync remote config

Aliases#

MethodSignatureDescription
RegisterAlias(alias, key string)Register a key alias; lookups of alias resolve to key

Value read API#

All Get* variants follow the same waterfall: override → changed pflags → env → config file → kvstore → defaults → pflag default.

Typed getters (global + method form for each)#

FunctionReturn typeNotes
Get(key string)anyGeneric; callers must assert the type
GetString(key)string
GetBool(key)bool
GetInt(key)int
GetInt32(key)int32
GetInt64(key)int64
GetUint8(key)uint8
GetUint(key)uint
GetUint16(key)uint16
GetUint32(key)uint32
GetUint64(key)uint64
GetFloat64(key)float64
GetTime(key)time.TimeVia cast
GetDuration(key)time.DurationVia cast
GetIntSlice(key)[]int
GetStringSlice(key)[]string
GetStringMap(key)map[string]anyNested subsection
GetStringMapString(key)map[string]string
GetStringMapStringSlice(key)map[string][]string
GetSizeInBytes(key)uintParses human-readable sizes like "1MB"

All type coercions go through github.com/spf13/cast, which gives silent best-effort conversion without panicking.

MethodSignatureDescription
Sub(key string) *ViperReturns a new *Viper scoped to a nested key; all its values are relative
AllKeys() []stringReturn all known keys (all layers, flattened)
AllSettings() map[string]anyReturn a deep map snapshot of all resolved values
IsSet(key string) boolTrue if the key exists in any layer at or above defaults
InConfig(key string) boolTrue if the key exists specifically in the config file layer

Value write API#

MethodSignatureDescription
Set(key string, value any)Write to the override map (highest precedence)
SetDefault(key string, value any)Write to the defaults map (lowest precedence)
SetTypeByDefaultValue(enable bool)When true, type-coerce env/flag values to match the default’s type

Unmarshal (struct binding) API#

FunctionSignatureDescription
Unmarshal(rawVal any, opts ...DecoderConfigOption) errorDecode the full config into a struct using mapstructure
UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) errorDecode a subsection into a struct
UnmarshalExact(rawVal any, opts ...DecoderConfigOption) errorLike Unmarshal but errors on unknown fields
DecodeHook(hook mapstructure.DecodeHookFunc) DecoderConfigOptionReturn a DecoderConfigOption that overrides the decode hook

The DecoderConfigOption type (func(*mapstructure.DecoderConfig)) is part of the public API, allowing callers to tune mapstructure’s behaviour without Viper exposing every knob.


Live reload API#

MethodSignatureDescription
OnConfigChange(run func(in fsnotify.Event))Register a callback invoked when the config file changes
WatchConfig()Start a background goroutine (uses fsnotify) watching the config file directory

WatchConfig handles Kubernetes ConfigMap atomic symlink swaps correctly by watching the directory rather than the file path.


Codec / extension API#

Interfaces#

InterfaceMethodsPurpose
EncoderEncode(v map[string]any) ([]byte, error)Serialize Viper’s internal map to a format
DecoderDecode(b []byte, v map[string]any) errorDeserialize bytes into Viper’s internal map
CodecEncoder + DecoderCombined encode/decode for a single format
EncoderRegistryEncoder(format string) (Encoder, error)Look up an encoder by format name
DecoderRegistryDecoder(format string) (Decoder, error)Look up a decoder by format name
CodecRegistryEncoderRegistry + DecoderRegistryCombined registry
FlagValueHasChanged() bool; Name() string; ValueString() string; ValueType() stringAbstraction over pflag (or any flag library)
FlagValueSetVisitAll(fn func(FlagValue))Iterate over a set of flags
FinderFind(fsys afero.Fs) ([]string, error)Locate config files in a filesystem
StringReplacerReplace(string) stringTransform env key names (satisfied by *strings.Replacer)
FileLookupErrorerror + fileLookup()Marker interface for config-file-not-found errors

Concrete types#

TypeConstructorPurpose
DefaultCodecRegistryNewCodecRegistry()Built-in codec registry; pre-wired with yaml/json/toml/dotenv
(r *DefaultCodecRegistry).RegisterCodec(format string, codec Codec) errorRegister a custom codec at runtime

Finders(finders ...Finder) Finder composes multiple Finder implementations into one (used with WithFinder).


Error types#

TypeImplementsDescription
ConfigFileNotFoundErrorFileLookupError (deprecated)Wraps FileNotFoundFromSearchError; preserved for backward compat
FileNotFoundFromSearchErrorFileLookupErrorConfig file not found in any search path
FileNotFoundErrorFileLookupErrorA specific config file path was not found
ConfigFileAlreadyExistsErrorerrorReturned by SafeWriteConfig* when file exists
ConfigMarshalErrorerrorFailed to encode the config (write path)
UnsupportedConfigErrorerrorFormat string has no registered codec

Callers can use errors.As / errors.Is against these concrete types, or errors.As against FileLookupError as a common supertype for any not-found scenario.


Debugging#

MethodSignatureDescription
Debug()Print a state dump to stdout
DebugTo(w io.Writer)Print a state dump to an arbitrary writer

Backward compatibility#

  • No explicit versioning strategy (v2 branch or go module major version suffix) in the current codebase; the module path is github.com/spf13/viper (v1).
  • Experimental features are introduced behind build-tag-gated compile-time feature flags (internal/features), and are activated via opt-in Option values (ExperimentalFinder(), ExperimentalBindStruct()). This allows unstable APIs to be tested without polluting the stable surface and without any runtime overhead for users who do not opt in.
  • ConfigFileNotFoundError was deprecated in favour of FileNotFoundFromSearchError; the old type is preserved and wraps the new one via Unwrap(), so errors.As chains work correctly for both old and new callers.

Key API design observations#

  1. Dual-surface pattern (global + instance) is explicit and zero-cost. Every global function is a one-liner wrapper. The design avoids the common mistake of duplicating logic for global vs. instance forms.

  2. FlagValue/FlagValueSet decouple the core from pflag. Consumers using other flag libraries (stdlib flag, urfave/cli, etc.) can implement the two-interface pair and bind without importing pflag. The concrete pflagValue/pflagValueSet wrappers ship with Viper but are not the only path.

  3. io.Reader/io.Writer for config I/O. ReadConfig(io.Reader) and WriteConfigTo(io.Writer) accept arbitrary byte streams, making it trivial to feed Viper from embedded files, HTTP responses, or test strings — no file on disk required.

  4. Sub(key) for scoped registries. Returns a new *Viper rooted at the given sub-key. All layer-awareness is preserved; this is the intended mechanism for component-local config without namespace collisions.

  5. Codec registry as the sole extension point for formats. DefaultCodecRegistry.RegisterCodec is the only extension seam for adding new file formats (e.g., HCL, INI). The built-in four (yaml, json, toml, dotenv) share the same Codec interface and live in internal/encoding/* — swappable in tests or in production via WithCodecRegistry.