confmaker

module
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: MIT

README

confmaker

CI Go Reference

Typed configuration for Go services, read from the environment and nowhere else (12factor III). Each library, each package and the application itself declares the config it needs as a plain struct; confmaker fills it, validates it, and hands it to Uber Fx.

  • Defaults in code, where the library's tests and callers see the same values a deployment starts from - not in a tag only the loader can read.
  • A misspelled variable fails the start, with a suggestion, instead of quietly leaving a field at its default.
  • What a declaration gets wrong is refused when it is bound - on the first start, whether or not the environment happens to set that variable.
  • A manifest of every variable the application reads, so a .env.example or a config map is generated from the types themselves.
  • Two dependencies: Fx and a secret type.
go get github.com/uchaloop/confmaker

Quick start

A library declares what it needs and reads nothing:

type Config struct {
	Host     string        `env:"HOST,notEmpty"`
	Timeout  time.Duration `env:"TIMEOUT"`
	Password secret.Secret `env:"PASSWORD"`
}

func (c *Config) SetDefaults() { c.Timeout = 30 * time.Second }

func (c Config) Validate() error {
	if c.Timeout <= 0 {
		return fmt.Errorf("timeout must be positive, got %s", c.Timeout)
	}

	return nil
}

The application names the instance, which gives the environment prefix:

fx.New(
	confx.Module(),

	confx.Provide[store.Config]("store"),       // STORE_HOST, STORE_TIMEOUT, ...
	confx.ProvideNamed[store.Config]("replica"),// REPLICA_HOST, REPLICA_TIMEOUT, ...
	confx.Provide[app.Config]("app"),           // APP_SHUTDOWN_WAIT, ...

	store.Module,
)

Provide gives the container an untagged value, the single default instance; ProvideNamed gives it a value tagged name:"replica", so a repository can ask for either. Nothing else in the process reads the environment.

What it catches

confx.Module() compares the environment against what the Provide calls declare:

unknown configuration variable "STORE_HSOT" (did you mean "STORE_HOST"?)

Everything a declaration itself can get wrong is refused before any value is read - a default written in a tag, an option the tag does not define, two fields claiming one variable, a config nested through a pointer or a slice, a field of a type that cannot be read from text.

Every problem is reported at once, one per line, each naming the config it belongs to:

config "store": required variable "STORE_HOST" is not set
config "store": timeout must be positive

What it prints

confx.Module(confx.WithDump(os.Stdout)) writes what the application actually read:

INSTANCE  VARIABLE            TYPE           VALUE          SOURCE
store     STORE_HOST          string         store:9000     env
store     STORE_PASSWORD      secret.Secret  (set)          env
store     STORE_TIMEOUT       time.Duration  30s            default

A secret is never printed: not here, not in the manifest, not in the error for a value that would not parse.

What it generates

confx.Manifest resolves the same list without building an application:

variables, err := confx.Manifest[store.Config]("store")
STORE_HOST=
STORE_TIMEOUT=30s
STORE_PASSWORD=

Each entry carries its Go type, whether it is required, whether it holds a secret, and its default rendered as text the variable could carry back. It is the same traversal that fills the config, so a variable it lists is exactly a variable the config reads.

Documentation

The tags, the rules and the reasons behind them are in the package documentation: pkg.go.dev/github.com/uchaloop/confmaker/confx.

Acknowledgements

I am grateful to the authors of Uber Fx, and to the authors of env, whose tag vocabulary this library follows and whose implementation it learned from.

License

MIT

Directories

Path Synopsis
Package confx builds a library's typed config from the environment and provides it into an Uber Fx container, so the library consumes a ready config without reading the environment itself.
Package confx builds a library's typed config from the environment and provides it into an Uber Fx container, so the library consumes a ready config without reading the environment itself.
Package validate provides a small error accumulator so a Validate method can report every problem in one pass instead of failing on the first.
Package validate provides a small error accumulator so a Validate method can report every problem in one pass instead of failing on the first.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL