Environment variables are Key-Value pairs on a system wide level, that can be accessed by running the processes on the system. Environment variables are often used to make the same program behave differently in different environments like PROD, DEV or TEST.
Why should you use environment variables?
• If you are entering the sensitive information in the code, then all the unauthorised users who have access to the code will have the sensitive information, that you might don’t want them to have.
• If you are managing these variables in one place. In case of any changes you don't have to change in all the places in the code.
• You can manage different environments like PROD,DEV or TEST. Environment variables are easy to change between deploys without changing the application code.
There are many packages for managing environments for this blog we are demonstrating the following packages :
• os package
• godotenv package
• viper package
There are many more packages for managing environments
os Package
Golang provides os package, an easy way to configure and access the environment variable.
To set the environment variable,
os.Setenv(key, value)
To get the environment variable,
value := os.Getenv(key)
Create a new file main.go inside the project.
package main
import (
"fmt"
"os"
)
// use os package to get the env variable which is already set
func envVariable(key string) string {
// set env variable using os package
os.Setenv(key, "gopher")
// return the env variable using os package
return os.Getenv(key)
}
func main() {
// os package
value := envVariable("name")
fmt.Printf("os package: name = %s \n", value)
fmt.Printf("environment = %s \n", os.Getenv("APP_ENV"))
}
Run the below command to check.
APP_ENV=prod go run main.go
// Output
os package: name = gopher
environment = prod
Benefit of using this library that we don’t have to maintain a env file and is easy to use.
GoDotEnv Package
The easiest way to load the .env file is using godotenv package.
Install Open the terminal in the project root directory.
go get github.com/joho/godotenv
godotenv provides a Load method to load the env files.
// Load the .env file in the current directory
godotenv.Load()
// or
godotenv.Load(".env")
Load method can load multiple env files at once. This also supports yaml.
Create a new .env file in the project root directory.
SERVER_PORT=8080
Update the main.go.
package main
import (
...
// Import godotenv
"github.com/joho/godotenv"
)
// use godot package to load/read the .env file and
// return the value of the key
func goDotEnvVariable(key string) string {
// load .env file
err := godotenv.Load(".env")
if err != nil {
log.Fatalf("Error loading .env file")
}
return os.Getenv(key)
}
func main() {
// godotenv package
dotenv := goDotEnvVariable("SERVER_PORT")
fmt.Printf("godotenv : %s = %s \n", "SERVER_PORT", dotenv)
}
Open the terminal and run the main.go.
go run main.go
// Output
godotenv : SERVER_PORT = 8080
Viper Package
Viper is one of the most popular packages in the golang community. Many Go projects are built using Viper including Hugo, Docker Notary, Mercury.
Install Open the terminal in the project root directory.
go get github.com/spf13/viper
To set the config file and path
viper.SetConfigFile(".env")
To read the config file
viper.ReadInConfig()
To get the value from the config file using key
viper.Get(key)
Update the main.go.
import (
"fmt"
"log"
"os"
"github.com/joho/godotenv"
"github.com/spf13/viper"
)
// use viper package to read .env file
// return the value of the key
func viperEnvVariable(key string) string {
// SetConfigFile explicitly defines the path, name and extension of the config file.
// Viper will use this and not check any of the config paths.
// .env - It will search for the .env file in the current directory
viper.SetConfigFile(".env")
// Find and read the config file
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("Error while reading config file %s", err)
}
// viper.Get() returns an empty interface{}
// to get the underlying type of the key,
// we have to do the type assertion, we know the underlying value is string
// if we type assert to other type it will throw an error
value, ok := viper.Get(key).(string)
// If the type is a string then ok will be true
// ok will make sure the program not break
if !ok {
log.Fatalf("Invalid type assertion")
}
return value
}
func main() {
// viper package read .env
viperenv := viperEnvVariable("SERVER_PORT")
fmt.Printf("viper : %s = %s \n", "SERVER_PORT", viperenv)
}
Viper is not limited to .env files. It supports:
• setting defaults
• reading from JSON, TOML, YAML, HCL, envfile and Java properties config files
• live watching and re-reading of config files (optional)
• reading from environment variables
• reading from remote config systems (etcd or Consul), and watching changes
• reading from command line flags
• reading from buffer
• setting explicit values
Viper can be thought of as a registry for all of your application's configuration needs.
visit our next post of the series migrations-in-golang