Loading configurations using Viper in Golang

Building a Golang application? Working with configurations? Then you should checkout the library Viper 🐍. Viper is a cool library for loading configurations into your Golang application. This post covers the basics of using Viper to load configuration into your golang application.

Golang library Viper to load configuration
Viper

What is Viper?

Viper is a library for Golang that provides complete configuration solution for your Go application following the third factor in The Twelve-Factor App principles. With Viper on our side, we do not have to worry about the configuration file types. It can load configurations from JSON, TOML, YAML, HCL, INI, envfile, Java properties formats or from remote sources. Furthermore, it can bind the properties, unmarshal it to structs, provide alias to the properties, read from flags with its companion Cobra, watch for changes to live reload config and even allows you to write configurations to the config file.

Why externalise configuration from code?

Separating configurations from code provides us below benefits.

  • Running the application in different environments like UAT, QA, CUG, Staging and Production without the need the of code change.
    • Imagine having database configuration inside your code. On deploying, this might require you to update your code as per the environment. Now externalising the config will loosely couple the config and code. Now you can run the same code with different versions of configuration for each environment.
    • Configuration can be externalised into environmental variables or external config servers using spring cloud config or AWS services like AWS App Config
  • It makes configurations easily manageable.
  • Multiple applications can use same configurations.
  • With live reload, configurations can be updated during runtime without requiring application restart.

Overall, developers can worry less about configurations and focus on more development. Now that we know what is Viper and why we should externalise configuration, let’s see how to use Viper to do job.

Installing viper in the project

Let’s start by installing Viper into the project. To install Viper in the project, run the below go get command. This will download the latest version of the library into the project.

go get github.com/spf13/viper

I have created a Golang project to cover the examples in this post. The examples are with Viper version v1.14.0.

Adding a new configuration file

Let’s create a new config file. I’m creating the below app.yml file under ./resources/app.yml path internally in the project.

application:
  name: thegeeksclan

Note: For this post, I have created the config file in the project internally. To follow the 12-factor app principle, I will externalise the config file following the Externalized configuration pattern in another post.

Loading configurations using Viper

Now that we have our new config file, let’s read it using Viper. I added the method loadConfigurations based on the examples from Viper’s github documentation.

func loadConfigurations() {
    viper.SetConfigName("app")
    viper.AddConfigPath("./resources")
    err := viper.ReadInConfig()
    if err != nil {
        panic(fmt.Errorf("fatal error config file: %w", err))
    }
}

Let’s see what each statement does.

  • viper.SetConfigName("app") – this is the name of your config file without the extension.
  • viper.AddConfigPath("./resources") – this provides the path where the config file is located.
  • viper.ReadInConfig() – locates and reads the config file into the memory

Now that we have loaded the configuration, let’s use it. I have updated the main method as shown below.

func main() {
    appName := viper.Get("application.name")
    fmt.Println("application name:", appName)
}

By invoking the method viper.Get("application.name") I’m reading the property into a local variable appName. Execute the command go run . to get the below output.

application name: thegeeksclan

Loading configuration file without extension

Let’s say I have the configurations in a file ./resources/app instead of ./resources/app.yml. The config file does not have an extension. In this case, it is mandatory to invoke the method viper.SetConfigType("yaml") before viper.ReadInConfig(). This defines the format of the config file. If this is missed, you will end up with below error .

Config File "app" Not Found in "/foo/bar/go-viper-example/resources"

Live reloading the configuration

Another support provided by Viper is live reloading. When enabled, viper watches the config files placed in filesystem of the application. Viper auto-reloads when configurations are updated externally without requiring the application to restart. To enable this feature, I have add the below lines in the loadConfigurations function.

func loadConfigurations() {
//	...
//	...
//	...
    viper.OnConfigChange(func(e fsnotify.Event) {
        fmt.Println("Config file changed:", e.Name)
    })
    viper.WatchConfig()
}

In the above code, the listener method viper.OnConfigChange listens to the changes in the file system using fsnotify.Event while viper.WatchConfig() watches the config file for changes and updates viper properties in-memory.

Working with env variables

Viper provides below methods to load properties from environment variables. I have covered this in depth in a separate post.

  • AutomaticEnv()
  • BindEnv(string...) : error
  • SetEnvPrefix(string)

Working with an alias

Viper provides the RegisterAlias method which can register an alias to a property. Below is an example.

func loadConfigurations() {
    ....
    viper.RegisterAlias("appName", "application.name")
    ....
}

In the above example, I have registered an alias appName for the property application.name. Hence, viper.Get("appName") can be used to get the same value as property application.name.

Unmarshalling the configuration

Viper supports unmarshalling the configuration into a struct or a map. For our example configuration, I have created the below struct.

type AppConfig struct {
    Application struct {
        Name string // to hold application.name
    }
    AppName string // for alias
}

Now that we have our struct, let’s add code to unmarshal it. This also works with an alias. In the above example, AppName field is for the alias appName which we introduced in our previous example. In below block, the method GetAppConfig invokes the function viper.Unmarshal which unmarshals the configuration into a struct AppConfig.

func getAppConfig() AppConfig {
    var appConfig AppConfig
    viper.Unmarshal(&appConfig)
    return appConfig
}

Now let’s update our main method and run the code.

func main() {
    appConfig := getAppConfig()
    fmt.Println("application name:", appConfig.Application.Name)
    fmt.Println("application name alias:", appConfig.AppName)	// prints with alias
}

The output of the above program will be,

application name: thegeeksclan
application name alias: thegeeksclan

Conclusion

In this post, we have seen what is Viper, how it will help us and how to use it. I have covered the basic features with examples but there are more to checkout. Check the official documentation for more cool methods and examples. 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 “Loading configurations using Viper in Golang”

  1. Pingback: Using Viper with environment variables 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 :)