dotenvgo

package module
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Mar 25, 2026 License: MIT Imports: 9 Imported by: 0

README

dotenvgo

Go Version License Zero Dependencies Coverage

Type-safe environment variables for Go with generics, struct tags, and isolated loaders.

Installation

go get github.com/godeh/dotenvgo

Requires Go 1.22 or newer.

Quick Examples

Type-Safe Variables
import "github.com/godeh/dotenvgo"

// With defaults
port := dotenvgo.New[int]("PORT").Default(8080).Get()
debug := dotenvgo.New[bool]("DEBUG").Default(false).Get()

// Required (panics if missing)
dbURL := dotenvgo.New[string]("DATABASE_URL").Required().Get()

// With error handling
apiKey, err := dotenvgo.New[string]("API_KEY").Required().GetE()

// Check existence
if dotenvgo.New[string]("FEATURE_FLAG").IsSet() {
    // ...
}

// Lookup with existence flag
value, exists := dotenvgo.New[string]("OPTIONAL").Lookup()
Struct Loading
type Config struct {
    Host     string        `env:"HOST" default:"localhost"`
    Port     int           `env:"PORT" default:"8080"`
    Debug    bool          `env:"DEBUG"`
    Timeout  time.Duration `env:"TIMEOUT" default:"30s"`
    Database string        `env:"DATABASE_URL" required:"true"`
    Hosts    []string      `env:"ALLOWED_HOSTS" sep:","`
}

var cfg Config
dotenvgo.Load(&cfg)

// Or with prefix (APP_HOST, APP_PORT, etc.)
dotenvgo.LoadWithPrefix(&cfg, "APP")

type DatabaseConfig struct {
    URL string `env:"URL"`
}

type AppConfig struct {
    DB DatabaseConfig `env:"DB"`
}

var appCfg AppConfig

// Reads DB_URL
dotenvgo.Load(&appCfg)

// Reads APP_DB_URL
dotenvgo.LoadWithPrefix(&appCfg, "APP")
Load .env Files
dotenvgo.LoadDotEnv(".env")        // Don't override existing vars
dotenvgo.LoadDotEnv(".env", true)  // Override existing vars
dotenvgo.MustLoadDotEnv(".env")    // Panic on error
Missing Vs Empty Values

dotenvgo distinguishes between a variable that is missing and a variable that is explicitly set to an empty string.

os.Unsetenv("DATABASE_URL")
dbURL := dotenvgo.New[string]("DATABASE_URL").Default("postgres://localhost").Get()
// dbURL == "postgres://localhost"

os.Setenv("DATABASE_URL", "")
dbURL = dotenvgo.New[string]("DATABASE_URL").Default("postgres://localhost").Get()
// dbURL == ""

type Config struct {
    DSN *string `env:"DATABASE_URL"`
}

var cfg Config
dotenvgo.Load(&cfg)
// cfg.DSN points to ""

This also affects required and .env loading:

  • required:"true" only fails when the variable is missing.
  • LoadDotEnv(path) will not overwrite an existing variable, even if its value is empty.
Custom Parsers
type LogLevel int

const (
    DEBUG LogLevel = iota
    INFO
    ERROR
)

// Register globally
dotenvgo.RegisterParser(func(s string) (LogLevel, error) {
    switch strings.ToLower(s) {
    case "debug": return DEBUG, nil
    case "info":  return INFO, nil
    case "error": return ERROR, nil
    default:      return 0, fmt.Errorf("invalid: %s", s)
    }
})

level := dotenvgo.New[LogLevel]("LOG_LEVEL").Default(INFO).Get()

// Slices are automatically supported!
// When you register a parser for T, []T works automatically
levels := dotenvgo.New[[]LogLevel]("LOG_LEVELS").Get() // "debug,info,error"
Isolated Loaders

Use separate loaders when different parts of your application need different parsing logic for the same type:

// Library A: "primary" = Blue
loaderA := dotenvgo.NewLoader()
loaderA.RegisterParser(func(s string) (Color, error) {
    if s == "primary" { return Blue, nil }
    return Color(s), nil
})

// Library B: "primary" = Red  
loaderB := dotenvgo.NewLoader()
loaderB.RegisterParser(func(s string) (Color, error) {
    if s == "primary" { return Red, nil }
    return Color(s), nil
})

// Each loader has its own registry
colorA := dotenvgo.WithLoader[Color](loaderA, "THEME").Get() // Blue
colorB := dotenvgo.WithLoader[Color](loaderB, "THEME").Get() // Red

Supported Types

Type Example Values
string any text
int, int8-64 42, -100
uint, uint8-64 42, 0
float32, float64 3.14, -0.5
bool true, false, 1, 0, yes, no, on, off
time.Duration 30s, 1h30m, 500ms
*time.Location America/New_York, UTC
*string, *int, *uint, *float64, *bool, *time.Duration same values as their non-pointer equivalents
*[]string, *[]int, *[]uint, *[]float64, *[]bool comma-separated values, or custom separators with sep
[]*string, []*int, []*uint, []*float64, []*bool comma-separated values, each item loaded as a pointer
*Struct nested config loaded from prefixed child fields such as DB_URL
[]string a,b,c
[]int, []int8-64 1,2,3
[]uint, []uint8-64 1,2,3
[]float32, []float64 1.5,2.5
[]bool true,false,1,0
Custom Via RegisterParser or encoding.TextUnmarshaler

Struct Tags

Tag Description Example
env Variable name, or nested struct prefix when used on a struct field env:"PORT" / env:"DB"
default Default value default:"8080"
required Fail if missing required:"true"
sep Slice separator sep:";"

Pointer fields are supported during struct loading. For scalar pointer fields such as *string or *int, the loader allocates the pointer when a value or default exists. Pointer-to-slice fields such as *[]string work the same way. Slice-of-pointer fields such as []*string and []*int are also supported for leaf types. For nested pointer structs such as *Database, the loader allocates the struct when at least one nested field is loaded, for example from DB_URL or nested defaults.

.env File Format

# Comments
KEY=value
MESSAGE="Hello World"
DEBUG=true # inline comment
export PORT=8080

# Multiline quoted values
CERT="line1
line2"

# Variable expansion (uses the current environment and variables loaded earlier in the file)
BASE=/app
CONFIG=${BASE}/config

Error Handling

err := dotenvgo.Load(&cfg)

var reqErr *dotenvgo.RequiredError
if errors.As(err, &reqErr) {
    log.Printf("Missing: %s", reqErr.Key)
}

var multiErr *dotenvgo.MultiError
if errors.As(err, &multiErr) {
    for _, e := range multiErr.Errors {
        log.Println(e)
    }
}

Utilities

dotenvgo.Set("KEY", "value")
dotenvgo.Unset("KEY")

allVars := dotenvgo.Export()
appVars := dotenvgo.ExportWithPrefix("APP")

Examples

See examples/ for complete working code:

Example Description
basic Simple variable access
struct Struct-based config
nested_prefix Nested structs with env tag prefixes
empty_values Missing vs empty value semantics
pointer_slices Pointer to slice and slice of pointers
file Loading .env files
expansion Variable expansion
isolated_loader Isolated loader demo

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultLoader = NewLoader()

DefaultLoader is the default loader instance used by global functions.

Functions

func Export

func Export() map[string]string

Export returns all environment variables as a map.

func ExportWithPrefix

func ExportWithPrefix(prefix string) map[string]string

ExportWithPrefix returns environment variables matching a prefix.

func Load

func Load(cfg any) error

Load populates a struct from environment variables using struct tags.

Supported tags:

  • env:"VAR_NAME" - the environment variable name
  • default:"value" - default value if not set
  • required:"true" - marks the field as required

Example:

type Config struct {
    Port     int           `env:"PORT" default:"8080"`
    Debug    bool          `env:"DEBUG" default:"false"`
    Database string        `env:"DATABASE_URL" required:"true"`
    Timeout  time.Duration `env:"TIMEOUT" default:"30s"`
}

func LoadDotEnv

func LoadDotEnv(path string, override ...bool) error

LoadDotEnv loads environment variables from a .env file. By default, it does NOT override existing environment variables. Pass true as the second argument to override existing variables.

Examples:

LoadDotEnv(".env")        // doesn't override existing vars
LoadDotEnv(".env", false) // same as above
LoadDotEnv(".env", true)  // overrides existing vars

func LoadWithPrefix

func LoadWithPrefix(cfg any, prefix string) error

LoadWithPrefix populates a struct with a prefix for all env vars. For example, LoadWithPrefix(cfg, "APP") will look for APP_PORT instead of PORT.

func MustLoad

func MustLoad(cfg any)

MustLoad is like Load but panics on error.

func MustLoadDotEnv

func MustLoadDotEnv(path string)

MustLoadDotEnv loads a .env file or panics.

func MustLoadWithPrefix

func MustLoadWithPrefix(cfg any, prefix string)

MustLoadWithPrefix is like LoadWithPrefix but panics on error.

func RegisterParser

func RegisterParser[T any](parser func(string) (T, error))

RegisterParser registers a custom parser for a specific type T. This parser will be used when loading structs with fields of type T.

func Set

func Set(key, value string)

Set sets an environment variable.

func Unset

func Unset(key string)

Unset removes an environment variable.

Types

type Loader added in v0.3.0

type Loader struct {
	// contains filtered or unexported fields
}

Loader manages the configuration loading and parser registry.

func NewLoader added in v0.3.0

func NewLoader() *Loader

NewLoader creates a new Loader with default parsers registered.

func (*Loader) Load added in v0.3.0

func (l *Loader) Load(cfg any) error

Load populates a struct from environment variables using struct tags.

func (*Loader) LoadWithPrefix added in v0.3.0

func (l *Loader) LoadWithPrefix(cfg any, prefix string) error

LoadWithPrefix populates a struct with a prefix for all env vars.

func (*Loader) RegisterParser added in v0.3.0

func (l *Loader) RegisterParser(parser any)

RegisterParser registers a custom parser for a specific type T on this Loader instance.

type MultiError

type MultiError struct {
	Errors []error
}

MultiError contains multiple errors from struct loading.

func (*MultiError) Error

func (e *MultiError) Error() string

func (*MultiError) Unwrap

func (e *MultiError) Unwrap() []error

Unwrap returns the list of errors.

type ParseError

type ParseError struct {
	Key   string
	Value string
	Err   error
}

ParseError is returned when an environment variable cannot be parsed.

func (*ParseError) Error

func (e *ParseError) Error() string

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

Unwrap returns the underlying error.

type RequiredError

type RequiredError struct {
	Key string
}

RequiredError is returned when a required environment variable is not set.

func (*RequiredError) Error

func (e *RequiredError) Error() string

type Var

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

Var represents an environment variable with type-safe access.

func New

func New[T any](key string) *Var[T]

New creates a new environment variable of type T using the default loader.

func NewVar added in v0.3.0

func NewVar[T any](l *Loader, key string) *Var[T]

NewVar creates a new environment variable of type T using the specified Loader. It searches for a registered parser or uses encoding.TextUnmarshaler.

func WithLoader added in v0.3.0

func WithLoader[T any](l *Loader, key string) *Var[T]

WithLoader creates a new environment variable of type T using the specified Loader. This provides a more fluent API for creating variables with isolated loaders.

Example:

loader := dotenvgo.NewLoader()
loader.RegisterParser(func(s string) (MyType, error) { ... })
value := dotenvgo.WithLoader[MyType](loader, "MY_VAR").Get()

func (*Var[T]) Default

func (v *Var[T]) Default(value T) *Var[T]

Default sets the default value if the environment variable is not set.

func (*Var[T]) Get

func (v *Var[T]) Get() T

Get returns the value of the environment variable. Panics if the variable is required but not set.

func (*Var[T]) GetE

func (v *Var[T]) GetE() (T, error)

GetE returns the value of the environment variable or an error.

func (*Var[T]) IsSet

func (v *Var[T]) IsSet() bool

IsSet returns whether the environment variable is set.

func (*Var[T]) Lookup

func (v *Var[T]) Lookup() (T, bool)

Lookup returns the value and whether it was set.

func (*Var[T]) MustGet

func (v *Var[T]) MustGet() T

MustGet returns the value or panics if there's an error. Alias for Get().

func (*Var[T]) Required

func (v *Var[T]) Required() *Var[T]

Required marks the environment variable as required. Get() will panic if the variable is not set. GetE() will return an error.

func (*Var[T]) WithPrefix

func (v *Var[T]) WithPrefix(prefix string) *Var[T]

WithPrefix adds a prefix to the environment variable key. For example, WithPrefix("APP").String("PORT") will look for "APP_PORT".

Directories

Path Synopsis
examples
basic command
empty_values command
Example: Missing versus empty environment values
Example: Missing versus empty environment values
expansion command
file command
isolated_loader command
Package main demonstrates the isolated loader feature of dotenvgo.
Package main demonstrates the isolated loader feature of dotenvgo.
nested_prefix command
Example: Nested structs using the parent env tag as a prefix
Example: Nested structs using the parent env tag as a prefix
pointer_slices command
Example: Pointer to slice and slice of pointers
Example: Pointer to slice and slice of pointers
struct command
Example: Struct-based configuration loading
Example: Struct-based configuration loading

Jump to

Keyboard shortcuts

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