In a previous post, I have explained how to use Viper. As a continuation, this post will cover on using Viper with environment variables in a Golang application. Just like loading configuration from a configuration file, using Viper, we can also load configuration from environment variables.
Viper provides three methods to work with environment variables.
SetEnvPrefix(string)
BindEnv(string...) : error
AutomaticEnv()
Let’s take a closer look at each method.
Using SetEnvPrefix
The method SetEnvPrefix
sets a prefix for the key to load config from environment variable. Both BindEnv
and AutomaticEnv
uses this function to load configuration. EnvPrefix helps when you want to load only few environment variables specific to your application and not all. By setting a prefix, Viper can load the environment variables with the configured prefix. For example, if you configure Viper to have TGC
as the prefix, then Viper will only load the environment variables that start with TGC_
.
Using BindEnv
The behaviour of the function BindEnv
changes with number of parameters passed. It is a Variadic function, where the first string is the key and rest are names of the environment variable that should map to the key. Let’s see how it behaves based on the number of params.
With a single param
In the below example, I have set up two environment variables and created two viper instances. The viper instance vPrefix
will demonstrate behaviour with a prefix. Likewise, the instance vNoPrefix
will demonstrate behaviour without a prefix. In case of single param, the key and the environment variable name are same. Viper looks for a specific format of environment variable name in this case.
func main() { singleParam() func singleParam() { // setting up env variables os.Setenv("TGC_DATAPREFIX", "data with prefix TGC") os.Setenv("DATA_NO_PREFIX", "got no prefix") vPrefix := viper.New() vPrefix.SetEnvPrefix("TGC") vPrefix.BindEnv("dataprefix") vNoPrefix := viper.New() vNoPrefix.BindEnv("data_no_prefix") fmt.Println("output 1:", vPrefix.Get("dataprefix")) fmt.Println("output 2:", vNoPrefix.Get("data_no_prefix")) }
With prefix
- If a prefix is defined, viper looks for an environment variable in the format ‘<
PREFIX>_<KEYNAME_IN_CAPS>
‘.- The line
vPrefix.SetEnvPrefix("TGC")
sets up the prefixTGC
. - In this case, the environment variable name should be configured as
TGC_DATAPREFIX
. - In the line
vPrefix.BindEnv("dataprefix")
, the key name isdataprefix
. - Note that the environment variable name is upper cased, i.e.,
TGC_DATAPREFIX
. With a prefix and a key name, viper looks up the environment variable in upper case.
- The environment variable
TGC_DATAPREFIX
is looked up and stored in the keydataprefix
. Which is accessed in the linevPrefix.Get("dataprefix")
.
- The line
Without prefix
- With no prefix, viper simply looks up the environment variable with the key name in upper case.
- In the line
vNoPrefix.BindEnv("data_no_prefix")
, key should beDATA_NO_PREFIX
which is upper case value of the key itself. - The line
vNoPrefix.BindEnv("data_no_prefix")
binds the env variableDATA_NO_PREFIX
with the keydata_no_prefix
. This is accessed byvNoPrefix.Get("data_no_prefix")
.
- In the line
The output of above program is
output 1: data with prefix TGC output 2: got no prefix
With double params
When using two params, the first param is the key and the second param is the environment variable name. The line vPrefix.BindEnv("data", "env_data")
retrieves the value from environment variable env_data
and set it to the key data
ignoring the prefix. Also, the environment variable name need not be in upper case.
func main() { doubleParam() } func doubleParam() { // setting up env variables os.Setenv("TGC_DATA", "data with prefix TGC") os.Setenv("env_data", "got no prefix") vPrefix := viper.New() vPrefix.SetEnvPrefix("TGC") vPrefix.BindEnv("data", "env_data") // does not append prefix and is not in full uppercase. vPrefix.BindEnv("data_prefix", "TGC_DATA") // explicitly mention full name of the param. prefix is ignored. fmt.Println("output 1:", vPrefix.Get("data")) fmt.Println("output 2:", vPrefix.Get("data_prefix")) }
The prefix set by SetEnvPrefix
is ignored. If you need to load a config that starts with a prefix like TGC
, use the full name of the environment variable like vPrefix.BindEnv("data_prefix", "TGC_DATA")
. This accesses the environment variable TGC_DATA
.
Output of the above program is
output 1: got no prefix output 2: data with prefix TGC
With multiple params
On passing multiple params, the first param is the key and the remaining are the different possible names of an environment variable. The names of environment variable takes up the precedence in the order they are provided.
func main() { multiParam() } func multiParam() { // setting up env variables os.Setenv("high_precedence", "precedence level 1") os.Setenv("low_precedence", "precedence level 2") os.Setenv("lower_precedence", "precedence level 3") v := viper.New() v.BindEnv("precedence", "no_env_variable_configured", "high_precedence", "low_precedence", "lower_precedence") fmt.Println("output:", v.Get("precedence")) }
In the above example, there is no environment variable set for no_env_variable_configured
. When BindEnv
is invoked, viper tries to looks up for all the four environment variables but only picks up high_precedence
since no_env_variable_configured
is unavailable. The value of high_precedence
is set to the key precedence
.
On executing the above go program, we would get the below output.
output: precedence level 1
Using AutomaticEnv
When using AutomaticEnv, Viper automatically binds the environment variables to the application. To demonstrate this, I have added two environment variables TGC_AUTO_1
and TGC_AUTO_2
in the below example. The property key we use to get the config depends the availability of the prefix. Let’s see how this works.
func main() { automatic() } func automatic() { // setting up env variables os.Setenv("TGC_AUTO_1", "an automatic env") os.Setenv("TGC_AUTO_2", "another automatic env") vPrefix := viper.New() vPrefix.SetEnvPrefix("TGC") vPrefix.AutomaticEnv() vNoPrefix := viper.New() vNoPrefix.AutomaticEnv() fmt.Println("output 1: ", vPrefix.Get("auto_1")) fmt.Println("output 2: ", vPrefix.Get("AUTO_1")) // case insensitive fmt.Println("output 3: ", vPrefix.Get("auto_2")) // when not using prefix, Get by full name fmt.Println("output 4: ", vNoPrefix.Get("TGC_AUTO_1")) fmt.Println("output 5: ", vNoPrefix.Get("tgc_auto_1")) // case insensitive fmt.Println("output 6: ", vNoPrefix.Get("TGC_AUTO_2")) }
With a prefix
In the above example, the Viper instance vPrefix
is to demonstrate AutomaticEnv
with a prefix. We are setting TGC
as the prefix in the statement vPrefix.SetEnvPrefix("TGC")
. As mentioned in BindEnv, the format used by Viper to retrieve prefixed config is ‘<PREFIX>_<KEYNAME_IN_CAPS>
‘. Hence for AutomaticEnv
, the key name should be auto_1
or AUTO_1
. Both lower case and upper case works since Viper changes the key name to upper internally.
Without a prefix
In case of no prefix, use the environment variable name as it is. Again here, the key name is case insensitive since Viper converts it to uppercase internally.
output 1: an automatic env output 2: an automatic env output 3: another automatic env output 4: an automatic env output 5: an automatic env output 6: another automatic env
Conclusion
In this post, we have seen how to use Viper with environment variables. Check the official documentation for more details. The above examples can be found in this Github repository. Also, checkout the other below posts about using Viper in Golang.
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: Loading configurations using Viper in Golang - The Geeks Clan
Pingback: Golang Viper - load multiple configuration files - The Geeks Clan