/konf

The simplest API for reading/watching config from file, env, flag and clouds (e.g. AWS, Azure, GCP).

Primary LanguageGoMIT LicenseMIT

A minimalist configuration API for Go

Go Version Go Reference Mentioned in Awesome Go Go Report Card Build Coverage

konf offers an(other) opinion on how Go programs can read configuration without becoming coupled to a particular configuration source. It contains two APIs with two different sets of users.

The Config type is intended for application authors. It provides a relatively small API which can be used everywhere you want to read configuration. It defers the actual configuration loading to the Loader interface.

The Loader and Watcher interface is intended for configuration source library implementers. They are pure interfaces which can be implemented to provide the actual configuration.

This decoupling allows application developers to write code in terms of *konf.Config while the configuration source(s) is managed "up stack" (e.g. in or near main()). Application developers can then switch configuration sources(s) as necessary.

Usage

Somewhere, early in an application's life, it will make a decision about which configuration source(s) (implementation) it actually wants to use. Something like:

    //go:embed config
    var config embed.FS

    func main() {
        // Create the Config.
        config := konf.New()

        // Load configuration from embed file system.
        if err := config.Load(fs.New(config, "config/config.json")); err != nil {
            // Handle error here.
        }
        // Load configuration from environment variables.
        if err := config.Load(env.New(env.WithPrefix("server"))); err != nil {
            // Handle error here.
        }

        // Watch the changes of configuration.
        go func() {
          if err := config.Watch(ctx); err != nil {
            // Handle error here.
          }
        }

        konf.SetDefault(config)

        // ... other setup code ...
    }

Outside of this early setup, no other packages need to know about the choice of configuration source(s). They read configuration in terms of functions in package konf:

    func (app *appObject) Run() {
        // Read the server configuration.
        type serverConfig struct {
            Host string
            Port int
        }
        cfg := konf.Get[serverConfig]("server")

        // Register callbacks while server configuration changes.
        konf.OnChange(func() {
          // Reconfig the application object.
        }, "server")

        // ... use cfg in app code ...
    }

Understand the configuration

While the configuration is loaded from multiple sources, static like environments or dynamic like AWS AppConfig, it's hard to understand where a final value comes from. The Config.Explain method provides information about how Config resolve each value from loaders for the given path. One example explanation is like:

config.nest has value [map] is loaded by map.
Here are other value(loader)s:
  - env(env)

Even more, the Config.Explain blurs sensitive information (e.g. password, secret, api keys) by default. It also can customize the blur behavior by providing a custom blur function using config.WithValueFormatter.

Configuration Providers

There are providers for the following configuration sources:

Custom Configuration Providers

You can implement your own provider by implementing the Loader for static configuration loader (e.g fs) or both Loader and Watcher for dynamic configuration loader (e.g. appconfig).

Inspiration

konf is inspired by spf13/viper and knadh/koanf. Thanks for authors of both awesome configuration libraries.