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
7
8[](https://github.com/avelino/awesome-go#configuration)
9[](https://repl.it/@sagikazarmark/Viper-example#main.go)
10
11[](https://github.com/spf13/viper/actions?query=workflow%3ACI)
12[](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
13[](https://goreportcard.com/report/github.com/spf13/viper)
14
15[](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