In a previous post, I covered on basic usage of the Golang library Viper. As of version v1.14.0, a single Viper instance supports loading configurations from only one configuration file. If our application work with multiple configuration files, then we have to manage this manually. This post covers how to load configurations from multiple configuration files using Viper in Golang.
The idea
The idea is fairly simple. Since one Viper instance can load configurations from only one configuration file, we will have multiple Viper instances loaded with configurations from multiple files. To access these instances easily, we will store all the Viper instances into a global map
against their configuration name. Finally to fetch value of a config key, we will expose a function that would fetch the Viper instance from the map
and then return the value from the instance.
Load from multiple configuration files
Let’s see with some examples. I will be using the same project that I created in my previous post to cover the examples. Here is the Github repo of the full project.
In the example project, I have three .yml
files under resources
package. Now, I will be loading the configurations from these files. In order to achieve this, I created a new config.go
file under config
package. As per our idea, I added logic to do the below.
- Firstly, to have a local map to store multiple instances of Viper per configuration
- Then, to load the configurations into the local map
- Finally, to retrieve the config property by the config file
Below is the entire code.
package config import ( "fmt" "github.com/spf13/viper" ) const ( App string = "app" Database string = "database" External string = "external" ) var configMap map[string]*viper.Viper func init() { configMap = make(map[string]*viper.Viper) } func LoadConfigs(configs ...string) { for _, configName := range configs { viperInstance := viper.New() viperInstance.SetConfigName(configName) viperInstance.SetConfigType("yaml") viperInstance.AddConfigPath("./resources") err := viperInstance.ReadInConfig() if err != nil { panic(fmt.Errorf("fatal error config file: %s %w", configName, err)) } configMap[configName] = viperInstance } } func GetByConfig(configName, key string) interface{} { instance, ok := configMap[configName] if !ok { return nil } return instance.Get(key) }
In the code, I have a global private map
variable configMap
which stores Viper instances against the config name. When the method LoadConfigs
is invoked with list of configurations to be loaded, it creates a new Viper instance against each configuration name and puts them in the map. For simplicity, I’m having the config name same as the .yml
file name. Finally, we have the GetByConfig
method which takes both config name and config key as inputs. It fetches the Viper instance using the config name and then returns the value corresponding to the key passed.
Testing the example
To test this, I have created below methods.
func init() { config.LoadConfigs(config.App, config.Database, config.External) } func RunMultipleConfig() { printConfig(config.App, "application.name") printConfig(config.Database, "database.host") printConfig(config.External, "external.http.getEmployee.url") } func printConfig(configName, key string) { fmt.Printf("value of '%s' from config '%s' is '%s'\n", key, configName, config.GetByConfig(configName, key)) }
As the initial step, on execution, the init
method will load all the three app
, database
and external
configurations into the map
. Now when I invoke the function RunMultipleConfig
, each line in the function will fetch and print values from different configuration files. Below is the output of the program.
value of 'application.name' from config 'app' is 'thegeeksclan' value of 'database.host' from config 'database' is 'localhost' value of 'external.http.getEmployee.url' from config 'external' is 'https://dummy.restapiexample.com/api/v1/employees'
Conclusion
With this, we have reached the end of our post. This is a simple approach to load configurations from multiple configuration files using Viper. Don’t miss the other posts below related to Viper. Stay tuned for more such content.
I hope this post was helpful 😊. If you find this post informative, support us by sharing this with fellow programmers in your circle 😀.
For any suggestions, improvements, reviews or if you like us to cover a specific topic, please leave a comment.
Follow us on twitter @thegeeksclan and in Facebook.
#TheGeeksClan #DevCommunity
Pingback: Using Viper with environment variables in Golang - The Geeks Clan
Pingback: Loading configurations using Viper in Golang - The Geeks Clan