Using Viper with environment variables in Golang

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 prefix TGC.
    • In this case, the environment variable name should be configured as TGC_DATAPREFIX.
    • In the line vPrefix.BindEnv("dataprefix"), the key name is dataprefix.
    • 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 key dataprefix. Which is accessed in the line vPrefix.Get("dataprefix").

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 be DATA_NO_PREFIX which is upper case value of the key itself.
    • The line vNoPrefix.BindEnv("data_no_prefix") binds the env variable DATA_NO_PREFIX with the key data_no_prefix. This is accessed by vNoPrefix.Get("data_no_prefix").

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

2 thoughts on “Using Viper with environment variables in Golang”

  1. Pingback: Loading configurations using Viper in Golang - The Geeks Clan

  2. Pingback: Golang Viper - load multiple configuration files - The Geeks Clan

Comments are closed.

error

Enjoy this blog? Please spread the word :)