Table of Contents
Introduction to golang viper
We typically need to employ multiple settings for development, testing, staging, and production environments when creating and deploying a backend web application. We will learn how to load configurations from files or environment variables using Viper
today.
Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed to work within an application and can handle all types of configuration needs and formats. 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
Firstly, you need to install the package:
go get github.com/spf13/viper
Using viper to read config from .env file
Wa have an .env
file:
PORT=3000
URL="https://test.golinuxcloud.com
USER_NAME="test_user"
PASSWORD="@#!@!#@#%!@#!@#!"
Here is an example of using Viper to read
that .env
file:
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.SetConfigFile(".env")
viper.ReadInConfig()
fmt.Println("port is: ", viper.Get("PORT"))
fmt.Println("username is: ", viper.Get("USER_NAME"))
}
Output:
port is: 3000
username is: test_user
Explanation:
- Read the .env file by using function:
viper.ReadInConfig()
- Read the environment values with
viper.Get("KEY"),
where KEY is the variable name. For example,viper.Get("PORT")
will read the PORT from the.env
file.
Using viper to read config from json file
In this example, we will read content of this json
file:
{
"compilerOptions": {
"module": "commonjs",
"target": "es6"
},
"exclude": ["node_modules"]
}
Consider to code below to understand how to work with json
file with viper
package main
import (
"fmt"
"github.com/spf13/viper"
)
func main() {
viper.AddConfigPath(".")
viper.SetConfigName("config") // Register config file name (no extension)
viper.SetConfigType("json") // Look for specific type
viper.ReadInConfig()
var config ConfigExample
viper.Unmarshal(&config)
fmt.Println(config.CompilerOptions.Target)
}
type ConfigExample struct {
CompilerOptions struct {
Module string `json:"module"`
Target string `json:"target"`
} `json:"compilerOptions"`
Exclude []string `json:"exclude"`
}
Output:
es6
Explanation:
- Using
viper.AddConfigPath()
to let viper know the location of the config file. - Call
viper.SetConfigName()
function to let viper know the config file name (without extension). viper.SetConfigFile():
the type of config file (.json).- Declare a new type
ConfigExample
struct. This struct will hold all configuration variables of the application that we read from file or environment variables. - Read the config by function
ReadInConfig()
function and parse the value to theConfigExample
struct.
Set default value when variable in undefined or empty
Method-1: Without using viper.SetDefault
Using golang viper we can also define a default or fallback variable value in situations when the variable has no value using viper.SetDefault
or we can manually also define a default value:
Here I have a sample environment file which I will source and read using viper:
# environment can be test,production,testing
APP_ENV=development
# username
DB_USER=postgres
# secure password
DB_PASS=pass
# app version not set
APP_VERSION=
Here is my sample go code where I will read the defined variables from app.env
while for variables where we don't have any value or for any missing variables, we will define a default value.
package main
import (
"fmt"
"github.com/spf13/viper"
)
//Function to read an environment or return a default value
func getEnvValue(key string, defaultValue string) string {
// Get file path
viper.SetConfigFile("app.env")
//read configs and handle errors
err := viper.ReadInConfig()
if err != nil {
fmt.Println(err)
}
value := viper.GetString(key)
if value != "" {
return value
}
return defaultValue
}
func main() {
// reading environments variable using the viper
appEnv := getEnvValue("APP_ENV", "defaultEnvtesting")
// not set in our app.env
appVersion := getEnvValue("APP_VERSION", "1")
dbPass := getEnvValue("DB_PASS", "1234")
dbUser := getEnvValue("DB_USER", "goLinux_DB")
serverAddress := getEnvValue("SERVER_ADDRESS", "127.0.0.1:8080")
fmt.Printf(" ------%s-----\n", "Reading Environment variables Using Viper package")
fmt.Printf(" %s = %s \n", "Application_Environment", appEnv)
fmt.Printf(" %s = %s \n", "Application_Version", appVersion)
fmt.Printf(" %s = %s \n", "Server_Listening_Address", serverAddress)
fmt.Printf(" %s = %s \n", "Database_User_Name", dbUser)
fmt.Printf(" %s = %s \n", "Database_User_Password", dbPass)
}
In the output as you can see, we are printing default value for SERVER_ADDRESS
and APP_VERSION
as these are not defined in app.env
:
# go run main.go ------Reading Environment variables Using Viper package----- Application_Environment = development Application_Version = 1 Server_Listening_Address = 127.0.0.1:8080 Database_User_Name = postgres Database_User_Password = pass
Method-2: Using viper.SetDefault
In this code I have an YAML based input file which we are reading using viper. Here is the content of creds.yaml
file:
---
credentials:
username: john
password: secret
This is the same code which reads creds.yaml
file and then loads the variables from the file. But in case there are variables which are not defined in out source file or if the values are empty then we will use viper.SetDefault
to define the default value for these variables:
package main
import (
"fmt"
"github.com/spf13/viper"
)
func userCredentials() {
fmt.Printf("Connecting to %s at %s\n",
viper.GetString("webserver.name"),
viper.GetString("webserver.endpoint"))
fmt.Printf("Your username is %s\n", viper.GetString("credentials.username"))
fmt.Printf("Your password is %s\n", viper.GetString("credentials.password"))
}
func main() {
// Define default values
viper.SetDefault("webserver.endpoint", "https://127.0.0.1/GET")
viper.SetDefault("webserver.name", "Testing Endpoint")
viper.AddConfigPath(".")
viper.SetConfigName("creds")
if err := viper.ReadInConfig(); err != nil {
panic(err)
}
userCredentials()
}
Output:
# go run main.go Connecting to Testing Endpoint at https://127.0.0.1/GET Your username is john Your password is secret
As you can see, we had not defined any value for webserver name and webserver endpoint in creds.yaml
file so viper has assigned the default value from the code.
Summary
In today's post, I have given 2 examples of reading config from .env
or json
file using viper in Golang. Some advantages of viper package:
- It can find, load, and unmarshal values from a config file.
- It supports many types of files, such as JSON, TOML, YAML, ENV, or INI.
- It can also read values from environment variables or command-line flags.
- It gives us the ability to set or override default values.
References
https://github.com/spf13/viper