confmaker

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: MIT Imports: 10 Imported by: 0

README

confmaker

CI Go Reference

Typed configuration for Go services with strict TOML decoding, environment overrides, validation, and Uber Fx integration.

Installation

go get github.com/uchaloop/confmaker

Configuration type

Use koanf tags for TOML fields and env tags for environment variables:

type Config struct {
	Addr     string        `koanf:"addr" env:"ADDR"`
	Timeout  time.Duration `koanf:"timeout"`
	Password secret.Secret `koanf:"-" env:"PASSWORD,notEmpty"`
}

Environment variables override TOML values. A field marked with koanf:"-" cannot be populated from TOML.

Fx

Environment directory

For applications with environment-specific files:

config/
├── common.toml   # optional
├── dev.toml
├── stage.toml
└── prod.toml

Set the deployment environment:

ENVIRONMENT=prod

Then pass only the directory:

fx.New(
	confx.LoadDir("config"),
	confx.ProvideDefault[otherlib.Config]("otherlib"),
	otherlib.Module(),
)

Supported values:

ENVIRONMENT File
dev dev.toml
stage stage.toml
prod prod.toml
prd prod.toml

The environment file is required. When common.toml exists, precedence is:

common.toml < environment file < field environment variables

Without Fx:

var cfg AppConfig
if err := confmaker.LoadDir(&cfg, "config"); err != nil {
	return err
}
Explicit files

Use LoadModule when file paths are selected by the application:

fx.New(
	confx.LoadModule(
		"config/common.toml",
		"config/prod.toml",
	),
	confx.ProvideDefault[otherlib.Config]("otherlib"),
	otherlib.Module(),
)

Files are applied in order; later files override earlier ones. confx.LoadModule() without arguments reads config.toml from the current directory and does not require ENVIRONMENT.

Default and named instances

Given:

[otherlib]
addr = "localhost:9000"

[analytics]
addr = "analytics:9000"

Provide the default instance untagged and the additional instance with an Fx name:

fx.New(
	confx.LoadModule("config.toml"),
	confx.ProvideDefault[otherlib.Config]("otherlib"),
	confx.Provide[otherlib.Config]("analytics", "analytics"),
)

Environment prefixes are derived from the section or instance name:

OTHERLIB_ADDR
ANALYTICS_ADDR

Override a prefix when needed:

confx.Provide[otherlib.Config](
	"analytics",
	"analytics",
	confx.WithEnvPrefix("REPORTING_"),
)
Environment-only configuration

When a configuration does not need a file:

fx.New(
	confx.ProvideNoFileDefault[otherlib.Config]("otherlib"),
	confx.ProvideNoFile[otherlib.Config]("analytics"),
)

Only fields with env tags are populated. ProvideNoFileDefault provides an untagged value; ProvideNoFile provides a named value.

Without Fx

Load one or more explicit TOML files:

type AppConfig struct {
	Service   ServiceConfig              `koanf:"service"`
	Instances map[string]otherlib.Config `koanf:"otherlib"`
}

var cfg AppConfig
if err := confmaker.Load(
	&cfg,
	"config/common.toml",
	"config/prod.toml",
); err != nil {
	return err
}

confmaker.Load(&cfg) reads config.toml from the current directory.

Decoding is strict:

  • unknown TOML keys return an error;
  • durations are accepted as strings such as "30s";
  • weak type conversion is disabled.

Secrets

Use github.com/uchaloop/secret/v2 for secret values:

type Config struct {
	Password secret.Secret `koanf:"-" env:"PASSWORD,notEmpty"`
}

Secrets are not loaded from TOML. With confx, fields with env tags are populated from environment variables.

Without Fx, read a required secret directly:

password, err := confmaker.ResolveSecret("OTHERLIB_PASSWORD")

Validation

confx calls Validate() error after applying file and environment values:

func (c Config) Validate() error {
	var errors validate.Errors
	errors.Require(c.Addr != "", "addr is required")
	errors.Require(c.Timeout > 0, "timeout must be positive")

	return errors.Err()
}

Named instances without Fx

Get a required entry from a map:

main, err := confmaker.Required(cfg.Instances, "otherlib", "main")

For names selected at runtime:

registry := confmaker.MakeRegistry(cfg.Instances)
instance, err := registry.Get("otherlib", name)
names := registry.Names()

Acknowledgements

I am grateful to the authors of Koanf, env, and Uber Fx. Their work made this library possible.

License

MIT

Documentation

Overview

Package confmaker is application-level configuration for Go services. A library declares its config as a plain struct with `koanf` (file) and `env` (environment) tags; the application loads the file and fills each library's struct, so a library never reads files or the environment itself.

Two entry points build on this root package. confmaker/confx wires configuration into Uber Fx - it loads the file once and provides each library its filled, validated config by name. Load (below) decodes a whole file into a struct for services that do not use Fx.

Infrastructure libraries need only the dependency-free secret type (github.com/uchaloop/secret/v2); importing this package or confx stays optional, for when a library wants more. The `koanf`/`env` tags a library declares are inert strings the app-level loader reads.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Load

func Load(dst any, paths ...string) error

Load reads the given TOML files in order and decodes the merged result into dst using the `koanf:"..."` struct tags. Later files override earlier ones, so a base + environment-overlay layout works by passing base first. With no paths, it reads config.toml from the current working directory.

Decoding is strict: an unknown key in the file (a typo, or a field that does not exist on dst) fails instead of being silently ignored. Values targeting exported secret.Value fields are ignored so a file can never populate them. Durations are parsed from strings such as "30m"; weak type coercion is disabled, so a mistyped value fails rather than being guessed.

func LoadDir added in v0.3.0

func LoadDir(dst any, dir string) error

LoadDir resolves configuration files from dir using the confmaker directory convention and decodes their merged contents into dst.

ENVIRONMENT is required and accepts dev, stage, prod, or prd (an alias for prod). An optional common.toml is loaded first; the required canonical environment file (dev.toml, stage.toml, or prod.toml) is loaded second and overrides common values.

func Required

func Required[T any](instances map[string]T, kind, name string) (T, error)

Required returns the instance named name from a map of configured instances, or an error naming what is missing. kind labels the resource in the error, for example "postgres" or "kafka consumer". Use it to turn a required-but-absent named connection into a clear startup failure.

func ResolveSecret

func ResolveSecret(envName string) (secret.Secret, error)

ResolveSecret reads a secret value from the named environment variable. It returns an error - naming only the variable, never the value - when the variable is unset or empty, so a missing secret fails the startup loudly without leaking anything into logs.

Types

type Registry

type Registry[T any] struct {
	// contains filtered or unexported fields
}

Registry holds a set of named instances of the same type, for resources whose count is only known at runtime. Prefer confx (Fx-wired named instances) for the fixed, mandatory instances of a service; use a Registry only for genuinely dynamic ones.

func MakeRegistry

func MakeRegistry[T any](items map[string]T) *Registry[T]

MakeRegistry returns a Registry over items. The map is used as-is.

func (*Registry[T]) Get

func (r *Registry[T]) Get(kind, name string) (T, error)

Get returns the instance named name, or an error naming what is missing. kind labels the resource in the error.

func (*Registry[T]) Names

func (r *Registry[T]) Names() []string

Names returns the configured instance names in sorted order.

Directories

Path Synopsis
Package confx wires configuration into Uber Fx.
Package confx wires configuration into Uber Fx.
internal
dirconfig
Package dirconfig resolves the convention-based configuration files shared by confmaker and confmaker/confx.
Package dirconfig resolves the convention-based configuration files shared by confmaker and confmaker/confx.
filedecode
Package filedecode contains decode hooks shared by confmaker's file loaders.
Package filedecode contains decode hooks shared by confmaker's file loaders.
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