A few months ago, I wrote an article about how to set environment variables in golang. It is important to set environment variables elegantly, so I created a simple function to do that.
The initial consideration was simple: if a corresponding config
file is provided, use os.Setenv
to set the key/value pairs inside. Then, throughout the entire app, the values can be directly accessed using os.Getenv
.
However, when it comes to formal deployment, several issues may arise:
- The configuration file may not necessarily be in YAML format; it could be in another format.
- Sometimes, it is desirable to inject environment variables through external services like Consul or etcd, but the current implementation uses
os.Getenv
. - The configuration file may need to be read from different paths.
- Variables can be set through the command line.
- And more unexpected issues.
For small projects, this approach actually solves my problems. However, it is not sufficient when more flexible configuration methods are required.
Fortunately, there is a mature solution in golang for all these issues called Viper.
VIPER
Viper is a powerful package for managing environment variables. It supports the following features:
- Default values
- Support for different configuration file formats (JSON, TOML, YAML)
- Ability to watch for changes in the configuration file (no need to restart the server!)
- Ability to fetch variables from remote servers (e.g., Consul or etcd)
- Ability to retrieve variables using command line flags
- Ability to retrieve variables from a reader, which means you can configure variables from different sources.
Basic Usage
First, use go get
or go mod
to download Viper.
go get github.com/spf13/viper
func main() {
viper.SetDefault("AWS_ACCESS_TOKEN", "AWS123456789") // Set default value
viper.SetConfigName("config") // Specify the config file name
viper.AddConfigPath("./config")
viper.ReadInConfig()
viper.AutomaticEnv()
}
SetDefault(key, value)
: Set default valueSetConfigName(name)
: Set the config file name. For example, if our config file is namedconfig.yml
, we just need to specifyconfig
. Why don't we need to specify the type? Viper automatically handles it for us.AddConfigPath
: Can set multiple directoriesReadInConfig
: Must be called to actually read the configurationAutomaticEnv
: Automatically syncs environment variables to Viper
Viper also provides many other functions for convenient configuration.
Retrieving Variables
After setting the variables, we can use viper.Get
to retrieve them. Get
returns an interface{}
, but Viper also provides type conversion functions such as viper.GetString
, viper.GetDuration
, viper.GetStringMap
, and so on.
func main() {
fmt.Println(viper.GetString("AWS_ACCESS_TOKEN"))
fmt.Println(viper.GetUint32("YOUR_INT"))
fmt.Println(viper.GetTime("YOUR_TIME"))
}
Conclusion
Setting variables can be challenging and complex, with more considerations than one might imagine. However, Viper handles many common use cases for us.
One important reminder is that when setting things like access tokens or secret keys, always remember to add them to .gitignore
to avoid accidentally pushing them to GitHub and causing regrets.