Confuse is a configuration library for Golang that is built on the idea of using a struct to manage your configuration data. This struct can then be populated with a fusion of configuration from any number of configuration sources such as environment variables, configuration files, and more.
The name Confuse was a merging between config and fusion, to indicate that two or more configuration files can be "fused" together in an override specification.
It is designed to be a lightweight, strongly typed and easy to use alternative to using map[string]interface{}
to load configuration data, or accessing fields using .Get()
from viper and parsing the types directly.
- Load configuration from
yaml
,json
,toml
andenv
files. - Load override configuration from environment variables.
- Load configuration from multiple sources, that will override each other in order of precedence.
- Load configuration into a struct, with specific data types for each field.
- Allow usage for
go-validator
to validate configuration data (validate:"required"
). - Generate a
json-schema
from your configuration struct to use with.json
and.yaml
files.
go get github.com/ls6-events/confuse
package main
import (
"fmt"
"github.com/davecgh/go-spew/spew"
"github.com/ls6-events/confuse"
)
type Config struct {
// `config` tag is not required, we will check for the field name in it's case, snake_case, kebab-case, camelCase and PascalCase forms.
Name string `config:"name"`
Port int `config:"port"`
// You can also use the `validate` tag to validate the configuration data.
Host string `config:"host" validate:"required"`
// You can also use nested structs.
Database struct {
Host string `config:"host"`
Port int `config:"port"`
} `config:"database"`
}
func main() {
// Create a new configuration struct.
var config Config
// Load the configuration from the default sources.
err := confuse.New(confuse.WithSourceFiles("./config.yaml")).Unmarshal(&config)
// Check for errors.
if err != nil {
panic(err)
}
// Print the configuration.
fmt.Printf("Name: %s\n", config.Name)
fmt.Printf("Port: %d\n", config.Port)
fmt.Printf("Host: %s\n", config.Host)
// Print the nested configuration.
spew.Dump(config.Database)
}
Check out more from the examples directory for different use cases.
There is an example of using multiple sources in the examples directory, but here is a quick example.
package main
import (
"fmt"
"github.com/ls6-events/confuse"
)
type Config struct {
Name string `config:"name"`
Port int `config:"port"`
}
func main() {
// Create a new configuration struct.
var config Config
// Load the configuration from the default sources.
err := confuse.New(
confuse.WithSourceFiles("./config.yaml", "./override.json"),
).Unmarshal(&config)
// Check for errors.
if err != nil {
panic(err)
}
// Print the configuration.
fmt.Printf("Name: %s\n", config.Name) // override
fmt.Printf("Port: %d\n", config.Port) // 8080
}
# config.yaml
name: "config"
port: 8080
// override.json
{
// This will override the name from the config.yaml file.
"name": "override"
}
You can have as many sources as you want, and they will be loaded in order of precedence. The last source will override the previous sources.
You can also load configuration from environment variables. This is done by using the confuse.WithSourceEnv()
option.
package main
import (
"fmt"
"github.com/ls6-events/confuse"
)
type Config struct {
Name string `config:"name"`
Port int `config:"port"`
}
func main() {
// Create a new configuration struct.
var config Config
// Load the configuration from the default sources.
err := confuse.New(
confuse.WithSourceFiles("./config.yaml"),
confuse.WithEnvironmentVariables(true),
).Unmarshal(&config)
// Check for errors.
if err != nil {
panic(err)
}
// Print the configuration.
fmt.Printf("Name: %s\n", config.Name) // override
fmt.Printf("Port: %d\n", config.Port) // 8080
}
This will allow the env variable NAME
to override the name
field in the configuration struct. The env variable PORT
will override the port
field in the configuration struct. If there is a nested struct, the joining operator of __
will be used to join the field names together. If you want to change this, there is the option confuse.WithEnvironmentVariablesSeparator(separator string)
function to change it.
You can also use the validate
tag to validate the configuration data. This is done by using the confuse.WithValidation(true)
option.
package main
import (
"fmt"
"github.com/ls6-events/confuse"
)
type Config struct {
Name string `config:"name" validate:"required"`
Port int `config:"port" validate:"required"`
}
func main() {
// Create a new configuration struct.
var config Config
// Load the configuration from the default sources.
err := confuse.New(
confuse.WithSourceFiles("./config.yaml"),
confuse.WithValidation(true),
).Unmarshal(&config)
// Check for errors.
if err != nil {
panic(err)
}
// Print the configuration.
fmt.Printf("Name: %s\n", config.Name) // override
fmt.Printf("Port: %d\n", config.Port) // 8080
}
There is an example of using validation in the examples directory. It will also use these tags to generate the correct JSON schema where appropriate.
You can also generate a JSON schema from your configuration struct. This is done by using the confuse.WithExactOutputJSONSchema(filepath string)
option.
package main
import (
"fmt"
"github.com/ls6-events/confuse"
)
type Config struct {
Name string `config:"name" validate:"required"`
Port int `config:"port" validate:"required"`
}
func main() {
// Create a new configuration struct.
var config Config
// Load the configuration from the default sources.
err := confuse.New(
confuse.WithSourceFiles("./config.yaml"),
confuse.WithExactOutputJSONSchema("./schema.json"),
).Unmarshal(&config)
// Check for errors.
if err != nil {
panic(err)
}
// Print the configuration.
fmt.Printf("Name: %s\n", config.Name) // override
fmt.Printf("Port: %d\n", config.Port) // 8080
}
There is an example of using validation in the examples directory. It will also use these tags to generate the correct JSON schema where appropriate.
Note, we also have an option to create a fuzzy schema, which will not include the required
tags (it's useful for setting the schema of override files as the nature of them may indicate that not all properties are set). This is done by using the confuse.WithFuzzyOutputJSONSchema(filepath string)
option.
You can also create your own custom sources. This can be done by using the confuse.WithSourceLoaders(func() (map[string]any, error))
option.
Note: this step happens before the unmarshalling to a struct. Therefore if you want it to override any existing configuration keys, you must preserve the case of the keys.
package main
import (
"fmt"
"github.com/ls6-events/confuse"
)
type Config struct {
Name string `config:"name"`
Port int `config:"port"`
}
func main() {
// Create a new configuration struct.
var config Config
// Load the configuration from the default sources.
err := confuse.New(
confuse.WithSourceFiles("./config.yaml"),
confuse.WithSourceLoaders(func() (map[string]any, error) {
return map[string]any{
"name": "override",
}, nil
}),
).Unmarshal(&config)
// Check for errors.
if err != nil {
panic(err)
}
// Print the configuration.
fmt.Printf("Name: %s\n", config.Name) // override
fmt.Printf("Port: %d\n", config.Port) // 8080
}
There is an example of using custom sources in the examples directory.