...

Text file src/github.com/spf13/viper/README.md

Documentation: github.com/spf13/viper

     1> ## Viper v2 feedback
     2> Viper is heading towards v2 and we would love to hear what _**you**_ would like to see in it. Share your thoughts here: https://forms.gle/R6faU74qPRPAzchZ9
     3>
     4> **Thank you!**
     5
     6![Viper](.github/logo.png?raw=true)
     7
     8[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration)
     9[![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go)
    10
    11[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI)
    12[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
    13[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
    14![Go Version](https://img.shields.io/badge/go%20version-%3E=1.19-61CFDD.svg?style=flat-square)
    15[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)
    16
    17**Go configuration with fangs!**
    18
    19Many Go projects are built using Viper including:
    20
    21* [Hugo](http://gohugo.io)
    22* [EMC RexRay](http://rexray.readthedocs.org/en/stable/)
    23* [Imgur’s Incus](https://github.com/Imgur/incus)
    24* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
    25* [Docker Notary](https://github.com/docker/Notary)
    26* [BloomApi](https://www.bloomapi.com/)
    27* [doctl](https://github.com/digitalocean/doctl)
    28* [Clairctl](https://github.com/jgsqware/clairctl)
    29* [Mercure](https://mercure.rocks)
    30* [Meshery](https://github.com/meshery/meshery)
    31* [Bearer](https://github.com/bearer/bearer)
    32* [Coder](https://github.com/coder/coder)
    33* [Vitess](https://vitess.io/)
    34
    35
    36## Install
    37
    38```shell
    39go get github.com/spf13/viper
    40```
    41
    42**Note:** Viper uses [Go Modules](https://github.com/golang/go/wiki/Modules) to manage dependencies.
    43
    44
    45## What is Viper?
    46
    47Viper is a complete configuration solution for Go applications including [12-Factor apps](https://12factor.net/#the_twelve_factors).
    48It is designed to work within an application, and can handle all types of configuration needs
    49and formats. It supports:
    50
    51* setting defaults
    52* reading from JSON, TOML, YAML, HCL, envfile and Java properties config files
    53* live watching and re-reading of config files (optional)
    54* reading from environment variables
    55* reading from remote config systems (etcd or Consul), and watching changes
    56* reading from command line flags
    57* reading from buffer
    58* setting explicit values
    59
    60Viper can be thought of as a registry for all of your applications configuration needs.
    61
    62
    63## Why Viper?
    64
    65When building a modern application, you don’t want to worry about
    66configuration file formats; you want to focus on building awesome software.
    67Viper is here to help with that.
    68
    69Viper does the following for you:
    70
    711. Find, load, and unmarshal a configuration file in JSON, TOML, YAML, HCL, INI, envfile or Java properties formats.
    722. Provide a mechanism to set default values for your different configuration options.
    733. Provide a mechanism to set override values for options specified through command line flags.
    744. Provide an alias system to easily rename parameters without breaking existing code.
    755. Make it easy to tell the difference between when a user has provided a command line or config file which is the same as the default.
    76
    77Viper uses the following precedence order. Each item takes precedence over the item below it:
    78
    79 * explicit call to `Set`
    80 * flag
    81 * env
    82 * config
    83 * key/value store
    84 * default
    85
    86**Important:** Viper configuration keys are case insensitive.
    87There are ongoing discussions about making that optional.
    88
    89
    90## Putting Values into Viper
    91
    92### Establishing Defaults
    93
    94A good configuration system will support default values. A default value is not
    95required for a key, but it’s useful in the event that a key hasn't been set via
    96config file, environment variable, remote configuration or flag.
    97
    98Examples:
    99
   100```go
   101viper.SetDefault("ContentDir", "content")
   102viper.SetDefault("LayoutDir", "layouts")
   103viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"})
   104```
   105
   106### Reading Config Files
   107
   108Viper requires minimal configuration so it knows where to look for config files.
   109Viper supports JSON, TOML, YAML, HCL, INI, envfile and Java Properties files. Viper can search multiple paths, but
   110currently a single Viper instance only supports a single configuration file.
   111Viper does not default to any configuration search paths leaving defaults decision
   112to an application.
   113
   114Here is an example of how to use Viper to search for and read a configuration file.
   115None of the specific paths are required, but at least one path should be provided
   116where a configuration file is expected.
   117
   118```go
   119viper.SetConfigName("config") // name of config file (without extension)
   120viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
   121viper.AddConfigPath("/etc/appname/")   // path to look for the config file in
   122viper.AddConfigPath("$HOME/.appname")  // call multiple times to add many search paths
   123viper.AddConfigPath(".")               // optionally look for config in the working directory
   124err := viper.ReadInConfig() // Find and read the config file
   125if err != nil { // Handle errors reading the config file
   126	panic(fmt.Errorf("fatal error config file: %w", err))
   127}
   128```
   129
   130You can handle the specific case where no config file is found like this:
   131
   132```go
   133if err := viper.ReadInConfig(); err != nil {
   134	if _, ok := err.(viper.ConfigFileNotFoundError); ok {
   135		// Config file not found; ignore error if desired
   136	} else {
   137		// Config file was found but another error was produced
   138	}
   139}
   140
   141// Config file found and successfully parsed
   142```
   143
   144*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmatically. For those configuration files that lie in the home of the user without any extension like `.bashrc`
   145
   146### Writing Config Files
   147
   148Reading from config files is useful, but at times you want to store all modifications made at run time.
   149For that, a bunch of commands are available, each with its own purpose:
   150
   151* WriteConfig - writes the current viper configuration to the predefined path, if exists. Errors if no predefined path. Will overwrite the current config file, if it exists.
   152* SafeWriteConfig - writes the current viper configuration to the predefined path. Errors if no predefined path. Will not overwrite the current config file, if it exists.
   153* WriteConfigAs - writes the current viper configuration to the given filepath. Will overwrite the given file, if it exists.
   154* SafeWriteConfigAs - writes the current viper configuration to the given filepath. Will not overwrite the given file, if it exists.
   155
   156As a rule of the thumb, everything marked with safe won't overwrite any file, but just create if not existent, whilst the default behavior is to create or truncate.
   157
   158A small examples section:
   159
   160```go
   161viper.WriteConfig() // writes current config to predefined path set by 'viper.AddConfigPath()' and 'viper.SetConfigName'
   162viper.SafeWriteConfig()
   163viper.WriteConfigAs("/path/to/my/.config")
   164viper.SafeWriteConfigAs("/path/to/my/.config") // will error since it has already been written
   165viper.SafeWriteConfigAs("/path/to/my/.other_config")
   166```
   167
   168### Watching and re-reading config files
   169
   170Viper supports the ability to have your application live read a config file while running.
   171
   172Gone are the days of needing to restart a server to have a config take effect,
   173viper powered applications can read an update to a config file while running and
   174not miss a beat.
   175
   176Simply tell the viper instance to watchConfig.
   177Optionally you can provide a function for Viper to run each time a change occurs.
   178
   179**Make sure you add all of the configPaths prior to calling `WatchConfig()`**
   180
   181```go
   182viper.OnConfigChange(func(e fsnotify.Event) {
   183	fmt.Println("Config file changed:", e.Name)
   184})
   185viper.WatchConfig()
   186```
   187
   188### Reading Config from io.Reader
   189
   190Viper predefines many configuration sources such as files, environment
   191variables, flags, and remote K/V store, but you are not bound to them. You can
   192also implement your own required configuration source and feed it to viper.
   193
   194```go
   195viper.SetConfigType("yaml") // or viper.SetConfigType("YAML")
   196
   197// any approach to require this configuration into your program.
   198var yamlExample = []byte(`
   199Hacker: true
   200name: steve
   201hobbies:
   202- skateboarding
   203- snowboarding
   204- go
   205clothing:
   206  jacket: leather
   207  trousers: denim
   208age: 35
   209eyes : brown
   210beard: true
   211`)
   212
   213viper.ReadConfig(bytes.NewBuffer(yamlExample))
   214
   215viper.Get("name") // this would be "steve"
   216```
   217
   218### Setting Overrides
   219
   220These could be from a command line flag, or from your own application logic.
   221
   222```go
   223viper.Set("Verbose", true)
   224viper.Set("LogFile", LogFile)
   225viper.Set("host.port", 5899)   // set subset
   226```
   227
   228### Registering and Using Aliases
   229
   230Aliases permit a single value to be referenced by multiple keys
   231
   232```go
   233viper.RegisterAlias("loud", "Verbose")
   234
   235viper.Set("verbose", true) // same result as next line
   236viper.Set("loud", true)   // same result as prior line
   237
   238viper.GetBool("loud") // true
   239viper.GetBool("verbose") // true
   240```
   241
   242### Working with Environment Variables
   243
   244Viper has full support for environment variables. This enables 12 factor
   245applications out of the box. There are five methods that exist to aid working
   246with ENV:
   247
   248 * `AutomaticEnv()`
   249 * `BindEnv(string...) : error`
   250 * `SetEnvPrefix(string)`
   251 * `SetEnvKeyReplacer(string...) *strings.Replacer`
   252 * `AllowEmptyEnv(bool)`
   253
   254_When working with ENV variables, it’s important to recognize that Viper
   255treats ENV variables as case sensitive._
   256
   257Viper provides a mechanism to try to ensure that ENV variables are unique. By
   258using `SetEnvPrefix`, you can tell Viper to use a prefix while reading from
   259the environment variables. Both `BindEnv` and `AutomaticEnv` will use this
   260prefix.
   261
   262`BindEnv` takes one or more parameters. The first parameter is the key name, the
   263rest are the name of the environment variables to bind to this key. If more than
   264one are provided, they will take precedence in the specified order. The name of
   265the environment variable is case sensitive. If the ENV variable name is not provided, then
   266Viper will automatically assume that the ENV variable matches the following format: prefix + "_" + the key name in ALL CAPS. When you explicitly provide the ENV variable name (the second parameter),
   267it **does not** automatically add the prefix. For example if the second parameter is "id",
   268Viper will look for the ENV variable "ID".
   269
   270One important thing to recognize when working with ENV variables is that the
   271value will be read each time it is accessed. Viper does not fix the value when
   272the `BindEnv` is called.
   273
   274`AutomaticEnv` is a powerful helper especially when combined with
   275`SetEnvPrefix`. When called, Viper will check for an environment variable any
   276time a `viper.Get` request is made. It will apply the following rules. It will
   277check for an environment variable with a name matching the key uppercased and
   278prefixed with the `EnvPrefix` if set.
   279
   280`SetEnvKeyReplacer` allows you to use a `strings.Replacer` object to rewrite Env
   281keys to an extent. This is useful if you want to use `-` or something in your
   282`Get()` calls, but want your environmental variables to use `_` delimiters. An
   283example of using it can be found in `viper_test.go`.
   284
   285Alternatively, you can use `EnvKeyReplacer` with `NewWithOptions` factory function.
   286Unlike `SetEnvKeyReplacer`, it accepts a `StringReplacer` interface allowing you to write custom string replacing logic.
   287
   288By default empty environment variables are considered unset and will fall back to
   289the next configuration source. To treat empty environment variables as set, use
   290the `AllowEmptyEnv` method.
   291
   292#### Env example
   293
   294```go
   295SetEnvPrefix("spf") // will be uppercased automatically
   296BindEnv("id")
   297
   298os.Setenv("SPF_ID", "13") // typically done outside of the app
   299
   300id := Get("id") // 13
   301```
   302
   303### Working with Flags
   304
   305Viper has the ability to bind to flags. Specifically, Viper supports `Pflags`
   306as used in the [Cobra](https://github.com/spf13/cobra) library.
   307
   308Like `BindEnv`, the value is not set when the binding method is called, but when
   309it is accessed. This means you can bind as early as you want, even in an
   310`init()` function.
   311
   312For individual flags, the `BindPFlag()` method provides this functionality.
   313
   314Example:
   315
   316```go
   317serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
   318viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
   319```
   320
   321You can also bind an existing set of pflags (pflag.FlagSet):
   322
   323Example:
   324
   325```go
   326pflag.Int("flagname", 1234, "help message for flagname")
   327
   328pflag.Parse()
   329viper.BindPFlags(pflag.CommandLine)
   330
   331i := viper.GetInt("flagname") // retrieve values from viper instead of pflag
   332```
   333
   334The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude
   335the use of other packages that use the [flag](https://golang.org/pkg/flag/)
   336package from the standard library. The pflag package can handle the flags
   337defined for the flag package by importing these flags. This is accomplished
   338by a calling a convenience function provided by the pflag package called
   339AddGoFlagSet().
   340
   341Example:
   342
   343```go
   344package main
   345
   346import (
   347	"flag"
   348	"github.com/spf13/pflag"
   349)
   350
   351func main() {
   352
   353	// using standard library "flag" package
   354	flag.Int("flagname", 1234, "help message for flagname")
   355
   356	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
   357	pflag.Parse()
   358	viper.BindPFlags(pflag.CommandLine)
   359
   360	i := viper.GetInt("flagname") // retrieve value from viper
   361
   362	// ...
   363}
   364```
   365
   366#### Flag interfaces
   367
   368Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`.
   369
   370`FlagValue` represents a single flag. This is a very simple example on how to implement this interface:
   371
   372```go
   373type myFlag struct {}
   374func (f myFlag) HasChanged() bool { return false }
   375func (f myFlag) Name() string { return "my-flag-name" }
   376func (f myFlag) ValueString() string { return "my-flag-value" }
   377func (f myFlag) ValueType() string { return "string" }
   378```
   379
   380Once your flag implements this interface, you can simply tell Viper to bind it:
   381
   382```go
   383viper.BindFlagValue("my-flag-name", myFlag{})
   384```
   385
   386`FlagValueSet` represents a group of flags. This is a very simple example on how to implement this interface:
   387
   388```go
   389type myFlagSet struct {
   390	flags []myFlag
   391}
   392
   393func (f myFlagSet) VisitAll(fn func(FlagValue)) {
   394	for _, flag := range flags {
   395		fn(flag)
   396	}
   397}
   398```
   399
   400Once your flag set implements this interface, you can simply tell Viper to bind it:
   401
   402```go
   403fSet := myFlagSet{
   404	flags: []myFlag{myFlag{}, myFlag{}},
   405}
   406viper.BindFlagValues("my-flags", fSet)
   407```
   408
   409### Remote Key/Value Store Support
   410
   411To enable remote support in Viper, do a blank import of the `viper/remote`
   412package:
   413
   414`import _ "github.com/spf13/viper/remote"`
   415
   416Viper will read a config string (as JSON, TOML, YAML, HCL or envfile) retrieved from a path
   417in a Key/Value store such as etcd or Consul.  These values take precedence over
   418default values, but are overridden by configuration values retrieved from disk,
   419flags, or environment variables.
   420
   421Viper supports multiple hosts. To use, pass a list of endpoints separated by `;`. For example `http://127.0.0.1:4001;http://127.0.0.1:4002`.
   422
   423Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve
   424configuration from the K/V store, which means that you can store your
   425configuration values encrypted and have them automatically decrypted if you have
   426the correct gpg keyring.  Encryption is optional.
   427
   428You can use remote configuration in conjunction with local configuration, or
   429independently of it.
   430
   431`crypt` has a command-line helper that you can use to put configurations in your
   432K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001.
   433
   434```bash
   435$ go get github.com/bketelsen/crypt/bin/crypt
   436$ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json
   437```
   438
   439Confirm that your value was set:
   440
   441```bash
   442$ crypt get -plaintext /config/hugo.json
   443```
   444
   445See the `crypt` documentation for examples of how to set encrypted values, or
   446how to use Consul.
   447
   448### Remote Key/Value Store Example - Unencrypted
   449
   450#### etcd
   451```go
   452viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001","/config/hugo.json")
   453viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
   454err := viper.ReadRemoteConfig()
   455```
   456
   457#### etcd3
   458```go
   459viper.AddRemoteProvider("etcd3", "http://127.0.0.1:4001","/config/hugo.json")
   460viper.SetConfigType("json") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
   461err := viper.ReadRemoteConfig()
   462```
   463
   464#### Consul
   465You need to set a key to Consul key/value storage with JSON value containing your desired config.
   466For example, create a Consul key/value store key `MY_CONSUL_KEY` with value:
   467
   468```json
   469{
   470    "port": 8080,
   471    "hostname": "myhostname.com"
   472}
   473```
   474
   475```go
   476viper.AddRemoteProvider("consul", "localhost:8500", "MY_CONSUL_KEY")
   477viper.SetConfigType("json") // Need to explicitly set this to json
   478err := viper.ReadRemoteConfig()
   479
   480fmt.Println(viper.Get("port")) // 8080
   481fmt.Println(viper.Get("hostname")) // myhostname.com
   482```
   483
   484#### Firestore
   485
   486```go
   487viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document")
   488viper.SetConfigType("json") // Config's format: "json", "toml", "yaml", "yml"
   489err := viper.ReadRemoteConfig()
   490```
   491
   492Of course, you're allowed to use `SecureRemoteProvider` also
   493
   494
   495#### NATS
   496
   497```go
   498viper.AddRemoteProvider("nats", "nats://127.0.0.1:4222", "myapp.config")
   499viper.SetConfigType("json")
   500err := viper.ReadRemoteConfig()
   501```
   502
   503### Remote Key/Value Store Example - Encrypted
   504
   505```go
   506viper.AddSecureRemoteProvider("etcd","http://127.0.0.1:4001","/config/hugo.json","/etc/secrets/mykeyring.gpg")
   507viper.SetConfigType("json") // because there is no file extension in a stream of bytes,  supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
   508err := viper.ReadRemoteConfig()
   509```
   510
   511### Watching Changes in etcd - Unencrypted
   512
   513```go
   514// alternatively, you can create a new viper instance.
   515var runtime_viper = viper.New()
   516
   517runtime_viper.AddRemoteProvider("etcd", "http://127.0.0.1:4001", "/config/hugo.yml")
   518runtime_viper.SetConfigType("yaml") // because there is no file extension in a stream of bytes, supported extensions are "json", "toml", "yaml", "yml", "properties", "props", "prop", "env", "dotenv"
   519
   520// read from remote config the first time.
   521err := runtime_viper.ReadRemoteConfig()
   522
   523// unmarshal config
   524runtime_viper.Unmarshal(&runtime_conf)
   525
   526// open a goroutine to watch remote changes forever
   527go func(){
   528	for {
   529		time.Sleep(time.Second * 5) // delay after each request
   530
   531		// currently, only tested with etcd support
   532		err := runtime_viper.WatchRemoteConfig()
   533		if err != nil {
   534			log.Errorf("unable to read remote config: %v", err)
   535			continue
   536		}
   537
   538		// unmarshal new config into our runtime config struct. you can also use channel
   539		// to implement a signal to notify the system of the changes
   540		runtime_viper.Unmarshal(&runtime_conf)
   541	}
   542}()
   543```
   544
   545## Getting Values From Viper
   546
   547In Viper, there are a few ways to get a value depending on the value’s type.
   548The following functions and methods exist:
   549
   550 * `Get(key string) : any`
   551 * `GetBool(key string) : bool`
   552 * `GetFloat64(key string) : float64`
   553 * `GetInt(key string) : int`
   554 * `GetIntSlice(key string) : []int`
   555 * `GetString(key string) : string`
   556 * `GetStringMap(key string) : map[string]any`
   557 * `GetStringMapString(key string) : map[string]string`
   558 * `GetStringSlice(key string) : []string`
   559 * `GetTime(key string) : time.Time`
   560 * `GetDuration(key string) : time.Duration`
   561 * `IsSet(key string) : bool`
   562 * `AllSettings() : map[string]any`
   563
   564One important thing to recognize is that each Get function will return a zero
   565value if it’s not found. To check if a given key exists, the `IsSet()` method
   566has been provided.
   567
   568The zero value will also be returned if the value is set, but fails to parse
   569as the requested type.
   570
   571Example:
   572```go
   573viper.GetString("logfile") // case-insensitive Setting & Getting
   574if viper.GetBool("verbose") {
   575	fmt.Println("verbose enabled")
   576}
   577```
   578### Accessing nested keys
   579
   580The accessor methods also accept formatted paths to deeply nested keys. For
   581example, if the following JSON file is loaded:
   582
   583```json
   584{
   585    "host": {
   586        "address": "localhost",
   587        "port": 5799
   588    },
   589    "datastore": {
   590        "metric": {
   591            "host": "127.0.0.1",
   592            "port": 3099
   593        },
   594        "warehouse": {
   595            "host": "198.0.0.1",
   596            "port": 2112
   597        }
   598    }
   599}
   600
   601```
   602
   603Viper can access a nested field by passing a `.` delimited path of keys:
   604
   605```go
   606GetString("datastore.metric.host") // (returns "127.0.0.1")
   607```
   608
   609This obeys the precedence rules established above; the search for the path
   610will cascade through the remaining configuration registries until found.
   611
   612For example, given this configuration file, both `datastore.metric.host` and
   613`datastore.metric.port` are already defined (and may be overridden). If in addition
   614`datastore.metric.protocol` was defined in the defaults, Viper would also find it.
   615
   616However, if `datastore.metric` was overridden (by a flag, an environment variable,
   617the `Set()` method, …) with an immediate value, then all sub-keys of
   618`datastore.metric` become undefined, they are “shadowed” by the higher-priority
   619configuration level.
   620
   621Viper can access array indices by using numbers in the path. For example:
   622
   623```jsonc
   624{
   625    "host": {
   626        "address": "localhost",
   627        "ports": [
   628            5799,
   629            6029
   630        ]
   631    },
   632    "datastore": {
   633        "metric": {
   634            "host": "127.0.0.1",
   635            "port": 3099
   636        },
   637        "warehouse": {
   638            "host": "198.0.0.1",
   639            "port": 2112
   640        }
   641    }
   642}
   643
   644GetInt("host.ports.1") // returns 6029
   645
   646```
   647
   648Lastly, if there exists a key that matches the delimited key path, its value
   649will be returned instead. E.g.
   650
   651```jsonc
   652{
   653    "datastore.metric.host": "0.0.0.0",
   654    "host": {
   655        "address": "localhost",
   656        "port": 5799
   657    },
   658    "datastore": {
   659        "metric": {
   660            "host": "127.0.0.1",
   661            "port": 3099
   662        },
   663        "warehouse": {
   664            "host": "198.0.0.1",
   665            "port": 2112
   666        }
   667    }
   668}
   669
   670GetString("datastore.metric.host") // returns "0.0.0.0"
   671```
   672
   673### Extracting a sub-tree
   674
   675When developing reusable modules, it's often useful to extract a subset of the configuration
   676and pass it to a module. This way the module can be instantiated more than once, with different configurations.
   677
   678For example, an application might use multiple different cache stores for different purposes:
   679
   680```yaml
   681cache:
   682  cache1:
   683    max-items: 100
   684    item-size: 64
   685  cache2:
   686    max-items: 200
   687    item-size: 80
   688```
   689
   690We could pass the cache name to a module (eg. `NewCache("cache1")`),
   691but it would require weird concatenation for accessing config keys and would be less separated from the global config.
   692
   693So instead of doing that let's pass a Viper instance to the constructor that represents a subset of the configuration:
   694
   695```go
   696cache1Config := viper.Sub("cache.cache1")
   697if cache1Config == nil { // Sub returns nil if the key cannot be found
   698	panic("cache configuration not found")
   699}
   700
   701cache1 := NewCache(cache1Config)
   702```
   703
   704**Note:** Always check the return value of `Sub`. It returns `nil` if a key cannot be found.
   705
   706Internally, the `NewCache` function can address `max-items` and `item-size` keys directly:
   707
   708```go
   709func NewCache(v *Viper) *Cache {
   710	return &Cache{
   711		MaxItems: v.GetInt("max-items"),
   712		ItemSize: v.GetInt("item-size"),
   713	}
   714}
   715```
   716
   717The resulting code is easy to test, since it's decoupled from the main config structure,
   718and easier to reuse (for the same reason).
   719
   720
   721### Unmarshaling
   722
   723You also have the option of Unmarshaling all or a specific value to a struct, map,
   724etc.
   725
   726There are two methods to do this:
   727
   728 * `Unmarshal(rawVal any) : error`
   729 * `UnmarshalKey(key string, rawVal any) : error`
   730
   731Example:
   732
   733```go
   734type config struct {
   735	Port int
   736	Name string
   737	PathMap string `mapstructure:"path_map"`
   738}
   739
   740var C config
   741
   742err := viper.Unmarshal(&C)
   743if err != nil {
   744	t.Fatalf("unable to decode into struct, %v", err)
   745}
   746```
   747
   748If you want to unmarshal configuration where the keys themselves contain dot (the default key delimiter),
   749you have to change the delimiter:
   750
   751```go
   752v := viper.NewWithOptions(viper.KeyDelimiter("::"))
   753
   754v.SetDefault("chart::values", map[string]any{
   755	"ingress": map[string]any{
   756		"annotations": map[string]any{
   757			"traefik.frontend.rule.type":                 "PathPrefix",
   758			"traefik.ingress.kubernetes.io/ssl-redirect": "true",
   759		},
   760	},
   761})
   762
   763type config struct {
   764	Chart struct{
   765		Values map[string]any
   766	}
   767}
   768
   769var C config
   770
   771v.Unmarshal(&C)
   772```
   773
   774Viper also supports unmarshaling into embedded structs:
   775
   776```go
   777/*
   778Example config:
   779
   780module:
   781    enabled: true
   782    token: 89h3f98hbwf987h3f98wenf89ehf
   783*/
   784type config struct {
   785	Module struct {
   786		Enabled bool
   787
   788		moduleConfig `mapstructure:",squash"`
   789	}
   790}
   791
   792// moduleConfig could be in a module specific package
   793type moduleConfig struct {
   794	Token string
   795}
   796
   797var C config
   798
   799err := viper.Unmarshal(&C)
   800if err != nil {
   801	t.Fatalf("unable to decode into struct, %v", err)
   802}
   803```
   804
   805Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default.
   806
   807### Decoding custom formats
   808
   809A frequently requested feature for Viper is adding more value formats and decoders.
   810For example, parsing character (dot, comma, semicolon, etc) separated strings into slices.
   811
   812This is already available in Viper using mapstructure decode hooks.
   813
   814Read more about the details in [this blog post](https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/).
   815
   816### Marshalling to string
   817
   818You may need to marshal all the settings held in viper into a string rather than write them to a file.
   819You can use your favorite format's marshaller with the config returned by `AllSettings()`.
   820
   821```go
   822import (
   823	yaml "gopkg.in/yaml.v2"
   824	// ...
   825)
   826
   827func yamlStringSettings() string {
   828	c := viper.AllSettings()
   829	bs, err := yaml.Marshal(c)
   830	if err != nil {
   831		log.Fatalf("unable to marshal config to YAML: %v", err)
   832	}
   833	return string(bs)
   834}
   835```
   836
   837## Viper or Vipers?
   838
   839Viper comes ready to use out of the box. There is no configuration or
   840initialization needed to begin using Viper. Since most applications will want
   841to use a single central repository for their configuration, the viper package
   842provides this. It is similar to a singleton.
   843
   844In all of the examples above, they demonstrate using viper in its singleton
   845style approach.
   846
   847### Working with multiple vipers
   848
   849You can also create many different vipers for use in your application. Each will
   850have its own unique set of configurations and values. Each can read from a
   851different config file, key value store, etc. All of the functions that viper
   852package supports are mirrored as methods on a viper.
   853
   854Example:
   855
   856```go
   857x := viper.New()
   858y := viper.New()
   859
   860x.SetDefault("ContentDir", "content")
   861y.SetDefault("ContentDir", "foobar")
   862
   863//...
   864```
   865
   866When working with multiple vipers, it is up to the user to keep track of the
   867different vipers.
   868
   869
   870## Q & A
   871
   872### Why is it called “Viper”?
   873
   874A: Viper is designed to be a [companion](http://en.wikipedia.org/wiki/Viper_(G.I._Joe))
   875to [Cobra](https://github.com/spf13/cobra). While both can operate completely
   876independently, together they make a powerful pair to handle much of your
   877application foundation needs.
   878
   879### Why is it called “Cobra”?
   880
   881Is there a better name for a [commander](http://en.wikipedia.org/wiki/Cobra_Commander)?
   882
   883### Does Viper support case sensitive keys?
   884
   885**tl;dr:** No.
   886
   887Viper merges configuration from various sources, many of which are either case insensitive or uses different casing than the rest of the sources (eg. env vars).
   888In order to provide the best experience when using multiple sources, the decision has been made to make all keys case insensitive.
   889
   890There has been several attempts to implement case sensitivity, but unfortunately it's not that trivial. We might take a stab at implementing it in [Viper v2](https://github.com/spf13/viper/issues/772), but despite the initial noise, it does not seem to be requested that much.
   891
   892You can vote for case sensitivity by filling out this feedback form: https://forms.gle/R6faU74qPRPAzchZ9
   893
   894### Is it safe to concurrently read and write to a viper?
   895
   896No, you will need to synchronize access to the viper yourself (for example by using the `sync` package). Concurrent reads and writes can cause a panic.
   897
   898## Troubleshooting
   899
   900See [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
   901
   902## Development
   903
   904**For an optimal developer experience, it is recommended to install [Nix](https://nixos.org/download.html) and [direnv](https://direnv.net/docs/installation.html).**
   905
   906_Alternatively, install [Go](https://go.dev/dl/) on your computer then run `make deps` to install the rest of the dependencies._
   907
   908Run the test suite:
   909
   910```shell
   911make test
   912```
   913
   914Run linters:
   915
   916```shell
   917make lint # pass -j option to run them in parallel
   918```
   919
   920Some linter violations can automatically be fixed:
   921
   922```shell
   923make fmt
   924```
   925
   926## License
   927
   928The project is licensed under the [MIT License](LICENSE).

View as plain text