configkit

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Apr 6, 2026 License: MIT Imports: 5 Imported by: 0

README

configkit

A thin Go config loader for Beaver projects. Wraps vendored copies of caarlos0/env and joho/godotenvzero external runtime dependencies.

import "github.com/gobeaver/configkit"

Why

  • Vendored, audited deps. No surprise upstream updates. Every version bump is reviewed line-by-line and recorded in env/CREDITS.md and dotenv/CREDITS.md.
  • Sensible defaults. Loads .env automatically, applies a BEAVER_ prefix.
  • All caarlos0/env features. Slices, maps, nested structs, required fields, custom unmarshalers, env var expansion.
  • Multi-instance via prefix. Run multiple configured instances of the same package side-by-side without YAML.

Quick start

package main

import (
    "fmt"
    "github.com/gobeaver/configkit"
)

type Config struct {
    Host string `env:"HOST" envDefault:"localhost"`
    Port int    `env:"PORT" envDefault:"8080"`
}

func main() {
    var cfg Config
    if err := configkit.Load(&cfg); err != nil {
        panic(err)
    }
    fmt.Printf("%+v\n", cfg)
}
# .env
BEAVER_HOST=example.com
BEAVER_PORT=3000

API

Function Purpose
configkit.Load(&cfg, opts...) error Parse env into a struct
configkit.MustLoad(&cfg, opts...) Same, panics on error (panic value is an error)
configkit.WithPrefix(p string) Override the default BEAVER_ prefix (use "" for none)
configkit.WithEnvFiles(files ...string) Choose which .env files to load
configkit.WithoutDotEnv() Skip .env loading entirely
configkit.WithRequired() Treat all undefaulted fields as required
.env semantics
  • A missing .env is silently ignored (the common case where projects don't ship one).
  • A malformed .env returns an error from Load rather than being swallowed.
  • With multiple files, first-wins: process env always takes precedence, then earlier files override later ones. To make .env.local win over .env, list it first:
    configkit.Load(&cfg, configkit.WithEnvFiles(".env.local", ".env"))
    

Multi-instance pattern

DEV_SLACK_WEBHOOK_URL=https://hooks.slack.com/dev
PROD_SLACK_WEBHOOK_URL=https://hooks.slack.com/prod

PRIMARY_DB_HOST=primary.db.example.com
REPLICA_DB_HOST=replica.db.example.com
devSlack    := slack.WithPrefix("DEV_").New()
prodSlack   := slack.WithPrefix("PROD_").New()

primaryDB   := database.WithPrefix("PRIMARY_").Init()
replicaDB   := database.WithPrefix("REPLICA_").Init()

Supported field types

Anything caarlos0/env supports:

type Config struct {
    Str      string        `env:"STR"`
    Int      int           `env:"INT"`
    Bool     bool          `env:"BOOL"`
    Duration time.Duration `env:"DURATION"`

    Hosts    []string          `env:"HOSTS" envSeparator:","`
    Ports    []int             `env:"PORTS" envSeparator:","`
    Metadata map[string]string `env:"METADATA"`

    APIKey string `env:"API_KEY,required"`
    Port   int    `env:"PORT" envDefault:"8080"`

    Database DatabaseConfig `envPrefix:"DB_"`
}

Combined with the package's default BEAVER_ prefix and the nested struct's envPrefix:"DB_", the database fields would read from BEAVER_DB_*.

Layout

configkit/
├── config.go            # Public API: Load, MustLoad, options
├── doc.go               # Package godoc
├── config_test.go
├── env/                 # Vendored caarlos0/env (see env/CREDITS.md)
├── dotenv/              # Vendored joho/godotenv (see dotenv/CREDITS.md)
└── docs/
    └── proposal.md      # Original design proposal (historical)

Vendor update process

  1. Wait. Don't update unless there's a security fix or a feature you need. For features, lag 3–6 months behind upstream.
  2. Diff. git clone the upstream repo and review every changed line between the current vendored tag and the target tag. Confirm no new external dependencies were introduced.
  3. Refresh.
    go mod download github.com/caarlos0/env/v11@<new-version>
    # Copy *.go (excluding *_test.go and example_*.go) into env/
    
    Upstream tests are deliberately excluded from the vendored tree: they pull in test-only build tags, exercise reflection on internal types that are easy to break when files are reorganized for vendoring, and would nearly double the on-disk footprint without testing anything we're actually consuming. The trade-off is that we don't catch upstream regressions automatically — which is exactly why every update goes through the line-by-line diff review in step 2.
  4. Record. Update env/CREDITS.md (or dotenv/CREDITS.md) with the new version, date, reviewer, and a short note on what changed upstream.
  5. Verify. go build ./... && go vet ./... && go test ./... from the configkit/ root.
Update checklist
  • Upstream version is 3+ months old (unless security fix)
  • No open security advisories on the target version
  • All changed lines reviewed
  • No new external dependencies added
  • All configkit tests still pass
  • CREDITS.md updated with version, date, reviewer

Tests

go test ./...

License

The wrapper code (config.go, doc.go, config_test.go) is MIT-licensed, copyright © 2026 Amedaz s.a.l. — see LICENSE. The vendored libraries retain their original MIT licenses — see env/LICENSE.md and dotenv/LICENCE.

Documentation

Overview

Package configkit provides environment variable configuration loading with automatic .env file support and configurable prefixes.

This package wraps vendored copies of github.com/caarlos0/env and github.com/joho/godotenv with sensible defaults for Beaver applications.

12-Factor Compliance

configkit follows the III. Config principle of the 12-factor app methodology (https://12factor.net/config): configuration is read strictly from the environment, never compiled in or stored in version-controlled config files. The optional .env loader is a developer-ergonomics layer only — process environment variables always take precedence over file values, so deployment platforms (Kubernetes, systemd, CI runners, secret managers) remain the source of truth in production.

Value precedence, highest to lowest:

  1. Process environment variables
  2. Earlier .env files listed in WithEnvFiles
  3. Later .env files
  4. envDefault struct tags

Basic Usage

type Config struct {
    Host string `env:"HOST" envDefault:"localhost"`
    Port int    `env:"PORT" envDefault:"8080"`
}

var cfg Config
configkit.Load(&cfg) // Uses BEAVER_ prefix, loads .env

Environment Variables

By default, all environment variables are prefixed with "BEAVER_":

BEAVER_HOST=example.com
BEAVER_PORT=3000

Custom Prefixes

Use WithPrefix for multi-instance configurations:

// Primary database: PRIMARY_DB_HOST, PRIMARY_DB_PORT
configkit.Load(&dbCfg, configkit.WithPrefix("PRIMARY_DB_"))

// Replica database: REPLICA_DB_HOST, REPLICA_DB_PORT
configkit.Load(&dbCfg, configkit.WithPrefix("REPLICA_DB_"))

// No prefix: DB_HOST, DB_PORT
configkit.Load(&dbCfg, configkit.WithPrefix(""))

Supported Types

All types supported by caarlos0/env are available:

type Config struct {
    // Basic types
    String   string        `env:"STRING"`
    Int      int           `env:"INT"`
    Bool     bool          `env:"BOOL"`
    Duration time.Duration `env:"DURATION"`

    // Slices
    Hosts []string `env:"HOSTS" envSeparator:","`

    // Required fields
    APIKey string `env:"API_KEY,required"`

    // Defaults
    Port int `env:"PORT" envDefault:"8080"`

    // Nested structs
    Database DatabaseConfig `envPrefix:"DB_"`
}

.env File Support

The package automatically loads .env files before parsing. Use WithEnvFiles to specify custom files:

configkit.Load(&cfg, configkit.WithEnvFiles(".env", ".env.local"))

Use WithoutDotEnv to disable automatic loading:

configkit.Load(&cfg, configkit.WithoutDotEnv())

Multi-Instance Pattern

The prefix pattern enables running multiple instances with different configs:

// Environment:
// DEV_SLACK_WEBHOOK_URL=https://hooks.slack.com/dev
// PROD_SLACK_WEBHOOK_URL=https://hooks.slack.com/prod

devSlack := slack.WithPrefix("DEV_").New()
prodSlack := slack.WithPrefix("PROD_").New()

This pattern avoids YAML complexity while remaining 12-factor compliant.

Index

Constants

View Source
const DefaultPrefix = "BEAVER_"

DefaultPrefix is the default environment variable prefix.

Variables

This section is empty.

Functions

func Load

func Load(cfg any, opts ...Option) error

Load parses environment variables into a struct. Automatically loads .env files first (can be disabled via options).

A missing .env file is not an error. A malformed one is.

Example:

type Config struct {
    Host string `env:"HOST" envDefault:"localhost"`
    Port int    `env:"PORT" envDefault:"8080"`
}

var cfg Config
configkit.Load(&cfg)                               // Uses BEAVER_ prefix
configkit.Load(&cfg, configkit.WithPrefix(""))     // No prefix
configkit.Load(&cfg, configkit.WithPrefix("APP_")) // Custom prefix

func MustLoad

func MustLoad(cfg any, opts ...Option)

MustLoad is like Load but panics on error. The panic value is an error wrapping the underlying cause, so callers can recover and use errors.Is / errors.As.

Types

type Option

type Option func(*Options)

Option configures Load behavior.

func WithEnvFiles

func WithEnvFiles(files ...string) Option

WithEnvFiles sets custom .env files to load.

Files are loaded with first-wins semantics: process env always takes precedence, then earlier files override later ones. To make a local override file win over a base file, list the override file first:

configkit.Load(&cfg, configkit.WithEnvFiles(".env.local", ".env"))

func WithPrefix

func WithPrefix(prefix string) Option

WithPrefix sets a custom environment variable prefix.

func WithRequired

func WithRequired() Option

WithRequired makes all fields required unless they have defaults.

func WithoutDotEnv

func WithoutDotEnv() Option

WithoutDotEnv disables automatic .env file loading.

type Options

type Options struct {
	// Prefix for environment variable names (default: "BEAVER_").
	Prefix string

	// EnvFiles to load before parsing (default: ".env"). Earlier entries
	// take precedence over later ones. See WithEnvFiles for the rationale.
	EnvFiles []string

	// SkipDotEnv skips loading .env files entirely. Useful in tests and
	// in environments where all configuration comes from the process env.
	SkipDotEnv bool

	// Required makes all fields required unless they have envDefault tags.
	// Equivalent to setting `,required` on every field.
	Required bool
}

Options configures the config loader.

Value precedence (highest to lowest):

  1. Process environment variables (set by the OS, container, shell, etc.)
  2. Earlier entries in EnvFiles (first file wins on conflict)
  3. Later entries in EnvFiles
  4. envDefault tag values on the target struct

Process env always wins over file contents — this matches 12-factor app conventions and lets deployment platforms (Kubernetes, systemd, CI) override committed defaults without editing files.

Directories

Path Synopsis
Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv)
Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv)
Package env is a simple, zero-dependencies library to parse environment variables into structs.
Package env is a simple, zero-dependencies library to parse environment variables into structs.

Jump to

Keyboard shortcuts

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