envconfig

package module
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

envconfig

A lightweight, type-safe Go library for loading configuration from environment variables into structs — with support for defaults, required fields, nested structs, prefix scoping, pointer types, maps, slices, validation, sensitive masking, custom types, time.Duration, and time.Time.

Go Reference License


Features

  • Generic & type-safe — uses Go generics (Load[T]()) for zero-boilerplate usage
  • Struct tags — declarative configuration via env, default, required, sensitive, sep, kvSep, envPrefix, and validate tags
  • Rich type support — all integer and float variants, bool, string, time.Duration, time.Time, pointer types, slices, and maps with string keys
  • Nested structs — recursive resolution with optional prefix scoping via envPrefix
  • Pointer fields — allocated on demand; left nil when the value is absent
  • Validation — built-in tag-based rules (min, max, oneof, regex, len, minlen, maxlen) and a custom Validator interface for post-parse logic
  • Standard interfaces — supports encoding.TextUnmarshaler for loading and encoding.TextMarshaler for display in Mask()
  • Custom types — implement Unmarshaler for full control over raw value parsing
  • Sensitive field masking — redact secrets from logs with Mask()
  • Aggregated errors — all missing/invalid fields are reported at once, with fully-qualified field paths

Installation

go get github.com/Shin-9x/envconfig

Requires Go 1.25.6 or later.


Quick Start

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/Shin-9x/envconfig"
)

type DatabaseConfig struct {
    Host string `env:"HOST" default:"localhost"`
    Port int    `env:"PORT" default:"5432" validate:"min=1,max=65535"`
}

type Config struct {
    Database DatabaseConfig `envPrefix:"DB_"`
    Debug    bool           `env:"APP_DEBUG"   default:"false"`
    Timeout  time.Duration  `env:"APP_TIMEOUT" default:"30s"`
    APIKey   string         `env:"API_KEY"     required:"true" sensitive:"true"`
    Tags     []string       `env:"APP_TAGS"    default:"web,api" comma:","`
    Labels   map[string]string `env:"APP_LABELS" sep:"," kvSep:":"`
}

func main() {
    cfg, err := envconfig.Load[Config]()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(envconfig.Mask(cfg))
    // Config: {DB_HOST=localhost, DB_PORT=5432, APP_DEBUG=false, APP_TIMEOUT=30s, API_KEY=[*****], APP_TAGS=[web,api], APP_LABELS=map[]}
}

Struct Tags Reference

Tag Description
env Environment variable name to read. Fields without this tag are skipped.
envPrefix Prefix prepended to all env keys within the tagged struct field. Prefixes are cumulative.
default Fallback value when the variable is unset or empty.
required Mark the field as required. Any non-empty tag value (e.g. "true") triggers the check.
sensitive Set to "true" to redact the value in Mask() output.
sep Separator for slice and map fields. Defaults to ",".
kvSep Key-value separator for map fields. Defaults to ":".
validate Comma-separated validation rules applied after the field is set (see Validation).

Supported Types

Go Type Example env value Notes
string hello
int 42
int32 / rune 65
int64 9223372036854775807
uint 10
uint8 / byte 255
uint64 18446744073709551615
float32 3.14
float64 2.718281828
bool true, false, 1, 0
time.Duration 30s, 1h, 500ms
time.Time 2024-01-15T10:30:00Z Parsed as RFC3339
*T any value for T nil if the variable is absent
[]T a,b,c Any of the above as element type
map[string]T key1:val1,key2:val2 String keys only

Nested Structs & Prefix Scoping

Nested structs are resolved recursively. Use envPrefix on a struct field to scope its children under a common prefix. Prefixes from outer and inner levels are concatenated.

type DatabaseConfig struct {
    Host string `env:"HOST" default:"localhost"`
    Port int    `env:"PORT" default:"5432"`
}

type Config struct {
    Database DatabaseConfig `envPrefix:"DB_"`
}
// DB_HOST → Config.Database.Host
// DB_PORT → Config.Database.Port

Prefixes nest naturally:

type Config struct {
    Prod struct {
        DB DatabaseConfig `envPrefix:"DB_"`
    } `envPrefix:"PROD_"`
}
// PROD_DB_HOST → Config.Prod.DB.Host

Struct types that implement Unmarshaler or encoding.TextUnmarshaler are treated as scalars and are not recursed into.


Pointer Fields

Fields with pointer types are allocated only when the environment variable is present and non-empty. If the variable is absent and the field is not required, the pointer remains nil.

type Config struct {
    MaxRetries *int `env:"MAX_RETRIES"` // nil if MAX_RETRIES is unset
}

Map Fields

Maps with string keys are supported. Items are split by sep (default ",") and each item is split into key and value by kvSep (default ":").

type Config struct {
    Labels map[string]string `env:"LABELS" sep:"," kvSep:":"`
}
// LABELS=env:prod,region:eu → map[env:prod region:eu]

The map value type can be any scalar type supported by the library.


Validation

Validation runs after a field is successfully set and only when the field has a non-empty value. Two mechanisms are available and can be combined freely.

Built-in Tag Rules

Declare rules in the validate struct tag as a comma-separated list of key=value pairs:

type Config struct {
    Port    int    `env:"PORT"     validate:"min=1,max=65535"`
    Level   string `env:"LOG_LVL"  validate:"oneof=DEBUG|INFO|WARN|ERROR"`
    Token   string `env:"TOKEN"    validate:"minlen=32,maxlen=128"`
    Code    string `env:"CODE"     validate:"regex=^[A-Z]{3}-[0-9]{4}$"`
    Pin     string `env:"PIN"      validate:"len=4"`
}
Rule Applies to Description
min numeric types Value must be ≥ the given bound (inclusive).
max numeric types Value must be ≤ the given bound (inclusive).
oneof string Value must match one of the pipe-separated options.
regex string Value must match the regular expression.
len string, slice, map Exact length required.
minlen string, slice, map Length must be ≥ the given bound.
maxlen string, slice, map Length must be ≤ the given bound.

Note on regex: Commas inside regex patterns are handled correctly — the parser splits only on commas that are immediately followed by another known rule name.

Custom Validator Interface

For logic that cannot be expressed with tags, implement Validator on your type:

type Validator interface {
    ValidateEnv() error
}

ValidateEnv is called after the field is set. Both value and pointer receivers are supported.

type Port struct{ value int }

func (p Port) ValidateEnv() error {
    if p.value < 1 || p.value > 65535 {
        return fmt.Errorf("port must be between 1 and 65535, got %d", p.value)
    }
    return nil
}

Custom Parsing via Unmarshaler

Implement Unmarshaler to control how a custom type is parsed from its raw string value:

type Unmarshaler interface {
    UnmarshalEnv(value string) error
}

Unmarshaler takes precedence over encoding.TextUnmarshaler when both are implemented.

type LogLevel struct{ level string }

func (l *LogLevel) UnmarshalEnv(value string) error {
    switch value {
    case "DEBUG", "INFO", "WARN", "ERROR":
        l.level = value
        return nil
    default:
        return fmt.Errorf("invalid log level %q, must be one of DEBUG, INFO, WARN, ERROR", value)
    }
}

type Config struct {
    Level LogLevel `env:"LOG_LEVEL" default:"INFO"`
}

The library also supports the standard encoding.TextUnmarshaler interface out of the box, without any additional setup.


Masking Sensitive Fields

Use Mask() to produce a log-safe string representation of your config. Fields tagged with sensitive:"true" are replaced with [*****]. The function handles nested structs, pointer fields, nil slices, and map fields transparently. Types that implement encoding.TextMarshaler are rendered via their MarshalText output.

cfg, _ := envconfig.Load[Config]()
fmt.Println(envconfig.Mask(cfg))
// Config: {DB_HOST=localhost, DB_PORT=5432, API_KEY=[*****], APP_TAGS=[web,api], APP_LABELS=map[env:prod, region:eu]}

// Also works with a pointer
fmt.Println(envconfig.Mask(&cfg))

Error Handling

Load collects all errors before returning, giving you a complete picture of every misconfigured field in a single run. Each error includes the fully-qualified field path:

Database.Host: missing required env var DB_HOST
Database.Port: value 99999 exceeds max=65535 for DB_PORT
cfg, err := envconfig.Load[Config]()
if err != nil {
    log.Fatal(err)
}

License

This project is licensed under the Apache License 2.0.

Documentation

Overview

Package envconfig provides a generic, reflection-based loader for populating configuration structs from environment variables.

The entry point is Load, which reads struct field tags to determine which environment variables to look up, how to parse them, and how to validate the resulting values.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Load

func Load[T any]() (T, error)

Load reads environment variables into a new value of type T and returns it. T must be a struct type; any other kind causes an immediate error.

Field mapping is controlled entirely by struct tags (see package-level tag constants). Nested structs are traversed recursively. Fields that implement Unmarshaler or encoding.TextUnmarshaler are delegated to their own unmarshalling logic.

All field errors are collected and returned together as a single joined error, so callers receive a complete picture of every misconfigured variable in one pass.

func Mask

func Mask(v any) string

Mask returns a human-readable representation of v, which must be a struct or a pointer to a struct. Fields tagged with sensitive:"true" are replaced with a fixed placeholder instead of their actual value, making the output safe for logging and diagnostics.

Nil pointers are rendered as "<nil>". Values that are not structs or struct pointers fall back to the default fmt formatting.

Types

type Unmarshaler

type Unmarshaler interface {
	UnmarshalEnv(value string) error
}

Unmarshaler is the interface implemented by types that can parse themselves from a raw environment variable string.

UnmarshalEnv receives the raw string value (never empty) and is responsible for populating the receiver. Return a descriptive error if the value is invalid.

Example:

type LogLevel struct{ level string }

func (l *LogLevel) UnmarshalEnv(value string) error {
    switch value {
    case "DEBUG", "INFO", "WARN", "ERROR":
        l.level = value
        return nil
    default:
        return fmt.Errorf("invalid log level %q, must be one of DEBUG, INFO, WARN, ERROR", value)
    }
}

type Validator added in v1.0.0

type Validator interface {
	ValidateEnv() error
}

Validator is the interface implemented by types that can validate themselves after being parsed from an environment variable string.

ValidateEnv is called after the value has been set on the field. Return a descriptive error if the value doesn't satisfy the constraint.

Example:

type Port struct{ value int }

func (p Port) ValidateEnv() error {
    if p.value < 1 || p.value > 65535 {
        return fmt.Errorf("port must be between 1 and 65535, got %d", p.value)
    }
    return nil
}

Jump to

Keyboard shortcuts

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