A year ago I came across a wonderful little Go library called configor. It did one thing well: it unmarshalled YAML, JSON, TOML, or shell env configuration files into Go structs. It supported a set of struct tags that defined validation rules and default field values. For example, you could define a struct like this:

type Config struct {
    ListenPort int `default:"8080"`

    Database struct {
        URL string `required:"true"`
    }
}

and the library would unmarshal its JSON/YAML/TOML representation, inject default values for any undefined fields, and ensure the presence of any fields marked required.

Unfortunately, it looks as if configor is unmaintained now. So, I decided to build upon the solid foundation @jinzhu laid and release configurer, configor’s spiritual successor.

Introducing configurer

configurer loads, unmarshals, and validates configuration files for Go programs. It supports loading JSON, YAML, and TOML configuration files from disk or HTTP URLs out-of-the-box, but can be extended to support additional file formats or data sources.

Like its predecessor, configurer uses struct tags to control default values, validation, and environment variable overrides for each config field:

type Config struct {
    ListenPort int `config:"default=8080,env=LISTEN_PORT"

    Database struct {
        URL string `config:"required"`
    }
}

Usage is simple:

// using the Config struct above
config := new(Config)
if err := configurer.LoadURL("file:///some-config.toml", &config); err != nil {
    log.Fatalf("failed to load config: %v", err)
}

// do something with your config

Additional Features

Extensible Data Sources

configurer can be extended to support reading configurations from data sources other than local files or HTTP URLs. To do this, implement the Source interface and call RegisterSource:

type S3Source struct {
}

func (s *S3Source) Protocols() []string {
    return []string{"s3"}
}

func (s *S3Source) Reader(url string) (io.ReadCloser, error) {
    // return io.ReadCloser for the config on s3
}

func init() {
    configurer.RegisterSource(new(S3Source))
}

Extensible Config Formats

The library can also be extended to support additional configuration formats by implementing the Unmarshaller interface and calling RegisterUnmarshaller:

type IniUnmarshaller struct {
}

func (in *IniUnmarshaller) Extensions() []string {
    return []string{"ini"}
}

func (in *IniUnmarshaller) ExtractFieldName() string {
    // code that reads custom field names from struct tags
}

func (in *IniUnmarshaller) Unmarshal(data []byte, v interface{}) error {
    // code that performs the unmarshalling
}

Handles Zero Values

configurer intelligently handles config parameters equal to the zero values of their Go types. Consider the following config struct:

type Config struct {
    NodeCount int `config:"default=10"`
}

A NodeCount of zero is technically the zero value of Go’s int type. configurer automatically determines whether a config parameter was set to a zero value explicitly, and applies defaults only in cases where parameters are left undefined.

What’s Next

Check out the source code and full documentation on GitHub. Pull requests and issues are warmly accepted.