Update 2024-02-01 #
I’ve used the package below for a handful of projects at work, but have since turned towards
Ardan Lab’s conf package.
It is really well thought out and automatically includes the config settings as ENV variables and command line flags, with a built in --help
menu.
If you haven’t seen this already, I highly recommend you check it out.
I decided to write a small thin wrapper around golobby/config. The projects at work tend to be copying and pasting a lot of the the same base code over and over again, and the configuration implementation is a common one. Also some projects use JSON for config, while others prefer YAML, and there are some secrets pulled in from GCP that are loaded as an environment variables. Either way, I wanted something that would satisfy all the little oddities and came up with this.
You can check out the full source code on Github
This project solves the following problems we were facing:
- Can support multiple file types (JSON, YAML, DOTENV)
- Requires some form of validation to ensure the config usable.
- OS Environment variables take presidence over values in config file(s).
It’s quite simple to use, as long as your config struct
satisfies the Config
interface.
The interface is very simplistic:
type Config interface {
Validate() error
}
Example Application #
A simple file server with a basic config.
package main
import (
"fmt"
"net"
"net/http"
"github.com/funayman/configer"
)
// appconfig is a simple example of what a configuration might look like for a
// server application. The `env` tags must be provided to be picked up by the
// OS's environment variables.
// To learn more refer to [golobby/env](https://github.com/golobby/env)
type appconfig struct {
Server struct {
Addr string `env:"SERVER_ADDR"`
Port int `env:"SERVER_PORT"`
}
Directory string `env:"DIRECTORY"`
}
// Validate satisfies the configer.Config interface
func (ac appconfig) Validate() error {
switch "" {
case ac.Server.Addr:
return fmt.Errorf("missing required config variable %q", "SEVER_ADDR")
case ac.Directory:
return fmt.Errorf("missing required config variable %q", "DIRECTORY")
}
if ac.Server.Port <= 0 || ac.Server.Port > 65535 {
return fmt.Errorf("%q must be between 1 and 65535", "SERVER_PORT")
}
return nil
}
var (
cfg appconfig
)
func main() {
if err := configer.Load(&cfg, "config.json"); err != nil {
panic(err)
}
// serve directory provided by config
http.Handle("/", http.FileServer(http.Dir(cfg.Directory)))
addr := net.JoinHostPort(
cfg.Server.Addr,
fmt.Sprintf("%d", cfg.Server.Port), // super lazy
)
fmt.Printf("serving directory %q on %s\n", cfg.Directory, addr)
if err := http.ListenAndServe(addr, nil); err != nil {
panic(err)
}
}