pureconf

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 27, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

README

pureconf

Go Reference Go Report Card codecov License

pureconf is a modern, generics-first, zero-configuration environment variable mapper for Go.

Designed specifically for the 12-Factor App methodology, it strictly focuses on environment variables, providing a lightweight and secure alternative to multi-format configuration managers.

Why pureconf? (vs. Viper)

spf13/viper is an incredible, battle-tested library and the undisputed king of Go configuration. Its strength lies in its comprehensive versatility—handling JSON, YAML, remote key/value stores, and live-reloading with ease.

However, if your project strictly follows the 12-Factor App methodology and relies exclusively on Environment Variables, you might not need a multi-format configuration engine.

pureconf is designed to be a focused, generics-native alternative for this specific use case:

Feature spf13/viper pureconf
Philosophy Comprehensive & Versatile Minimalist & Env-Vars strictly
Type System map[string]any + runtime casting 100% Static typing via Generics ([T any])
Secret Management Standard string handling Secret[T] auto-masks in logs to prevent leaks
Mapping Setup Requires mapstructure tags Zero-Config (Auto-infers from struct names)
Default Values Imperative setup via code Declarative via default:"..." struct tags
Error Handling Fails on the first encountered error Aggregates ALL errors at once via errors.Join

By narrowing the focus solely to environment variables and leveraging Go 1.21+, pureconf provides a lightweight, type-safe, and highly secure developer experience.

Features

  • Zero-Configuration: No env:"..." tags required. It automatically maps AppConfig.DB.Port to APP_DB_PORT.
  • Built-in Secret Masking: Use the Secret[T] type for passwords/tokens. They are automatically masked as *** when printed or logged, completely preventing accidental credential leaks.
  • Declarative Defaults: Set fallback values directly in your struct using default:"..." tags.
  • Recursive Nested Structs: Infinitely nest your configuration models natively.
  • Comprehensive Error Aggregation: Doesn't stop at the first typo. It validates everything and returns an aggregated list of all missing/invalid fields at once.

Installation

go get github.com/maeshinshin/pureconf

Requires Go 1.21 or later.

Quick Start

Define your configuration purely using Go structs. No tags are needed unless you want to override the default naming convention or provide fallback values.

package main

import (
    "fmt"
    "log"
    "log/slog"

    "github.com/maeshinshin/pureconf"
)

// 1. Define your configuration structure
type DBConfig struct {
    Host string                `default:"127.0.0.1"` // Falls back to 127.0.0.1 if MYAPP_DB_HOST is unset
    Port int                   `default:"5432"`      // Falls back to 5432 if MYAPP_DB_PORT is unset
    Pass pureconf.Secret[string] // Maps to MYAPP_DB_PASS (Secured!)
}

type AppConfig struct {
    Debug bool     `default:"false"` // Maps to MYAPP_DEBUG
    DB    DBConfig // Automatically creates the "DB_" namespace
}

func main() {
    // 2. Load configuration from environment variables
    // Using WithEnvPrefix("MYAPP_") prevents collision with system env vars.
    cfg, err := pureconf.Load[AppConfig](pureconf.WithEnvPrefix("MYAPP_"))
    if err != nil {
        log.Fatalf("Configuration failed:\n%v", err)
    }

    // 3. Secrets are protected by default!
    slog.Info("Loaded configuration", "db_password", cfg.DB.Pass) 
    // Output: "db_password": "***" (Prevents accidental log leaks)

    // 4. Explicitly unmask when you actually need to use the secret
    connectToDB(cfg.DB.Host, cfg.DB.Port, cfg.DB.Pass.Unmask())
}

func connectToDB(host string, port int, password string) {
    fmt.Printf("Connecting to %s:%d...\n", host, port)
}

Default Values & Priorities

You can easily set fallback values using the default:"..." struct tag.

pureconf respects the following priority hierarchy (from highest to lowest):

  1. Environment Variables: Always takes absolute precedence.
  2. Pre-initialized Struct Values: If you set a non-zero value in your struct before calling pureconf.Load(), it overrides the default tag.
  3. default Tags: Used only if the environment variable is missing and the field is at its zero value.
type ServerConfig struct {
    Port int `default:"8080"`
}

// Example 1: Standard load (Port becomes 8080 via default tag)
cfg1, _ := pureconf.Load[ServerConfig]() 

// Example 2: Pre-initialized value takes precedence (Port remains 9090)
// The `default:"8080"` tag is ignored because the value is already set.
cfg2 := &ServerConfig{Port: 9090}
pureconf.Load(cfg2) 

Error Aggregation

If multiple environment variables are invalid or missing, pureconf tells you exactly what went wrong all at once, saving you from the frustrating "fix one, recompile, find the next error" loop.

cfg, err := pureconf.Load[AppConfig](pureconf.WithEnvPrefix("MYAPP_"))
if err != nil {
    // Returns aggregated errors like:
    // failed to parse 'not-a-number' as int for field Port: invalid syntax
    // failed to parse 'yes' as bool for field Debug: invalid syntax
}

License

Apache License 2.0 - See LICENSE for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Load

func Load[T any](opts ...Option) (*T, error)

Types

type Option

type Option func(*options)

func WithEnvPrefix

func WithEnvPrefix(p string) Option

type ParseError

type ParseError = envmap.ParseError

type Secret

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

func (*Secret[T]) IsSensitive

func (s *Secret[T]) IsSensitive() bool

func (Secret[T]) LogValue

func (s Secret[T]) LogValue() slog.Value

func (Secret[T]) String

func (s Secret[T]) String() string

func (*Secret[T]) UnmarshalText

func (s *Secret[T]) UnmarshalText(text []byte) error

func (Secret[T]) Unmask

func (s Secret[T]) Unmask() T

type UnsupportedTypeError

type UnsupportedTypeError = envmap.UnsupportedTypeError

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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