Package Functions
Package-level convenience functions provide a simple API suitable for most use cases. These functions use the global default loader, and all are thread-safe.
INFO
The global default loader must be explicitly initialized via Load() or LoadWithConfig() — it is not auto-created on first call. If not initialized, function behavior is as follows:
Get*functions (GetString,GetInt,GetBool, etc.): return the passed default value (or zero value)Lookup: returns("", false)Keys/All/Len/GetSecure: returnnil/0Set/Delete/Validate/ParseInto: returnErrNotInitialized
Load Functions
Load
func Load(filenames ...string) errorLoads environment variable files and applies them to the system environment.
Parameters:
filenames- list of file paths. When not provided, defaults to loading the.envfile (usesDefaultConfig()'sFilenamessetting).
Returns:
error- load error
Behavior:
- Creates a new Loader instance and sets it as the default loader
- Automatically applies to the system environment (
os.Environ) - Later-loaded files can overwrite earlier ones (controlled by the
OverwriteExistingconfig;Load()defaults tofalse, i.e., no overwriting) - Returns
ErrAlreadyInitializedif the default loader is already initialized - Supports multiple formats (.env, JSON, YAML)
// Load .env file
if err := env.Load(".env"); err != nil {
log.Fatal(err)
}
// Load specified files (in order; to overwrite, set OverwriteExisting)
if err := env.Load(".env", ".env.local", "config.json"); err != nil {
log.Fatal(err)
}
// JSON/YAML nested structures support dot-path access
// config.json: {"database": {"host": "localhost", "port": 5432}}
env.Load("config.json")
host := env.GetString("database.host") // "localhost"
port := env.GetInt("database.port") // 5432Key Resolution
All getter functions support smart key resolution, providing flexible access methods.
Resolution Rules
1. Exact match (priority)
// .env: APP_NAME=myapp
name := env.GetString("APP_NAME") // "myapp"2. Uppercase conversion (simple keys)
// For keys without dots, the uppercase version is tried automatically
name := env.GetString("app_name") // Looks up app_name -> APP_NAME3. Dot-path resolution (nested keys)
// JSON: {"app": {"name": "myapp"}}
// Stored as: APP_NAME=myapp
// All of the following can access this value
name := env.GetString("APP_NAME") // Flat key (recommended)
name := env.GetString("app.name") // Dot path (auto-converted)
name := env.GetString("APP.NAME") // Uppercase dot pathPath Conversion Table
| Input Key | Stored Key |
|---|---|
"database.host" | "DATABASE_HOST" |
"db.port" | "DB_PORT" |
"servers.0.host" | "SERVERS_0_HOST" |
"app.config.name" | "APP_CONFIG_NAME" |
Index Access
Array elements can be accessed by index, or fall back to comma-separated values:
// JSON: {"servers": [{"host": "a.com"}, {"host": "b.com"}]}
// Stored as: SERVERS_0_HOST=a.com, SERVERS_1_HOST=b.com
host0 := env.GetString("servers.0.host") // "a.com"
host1 := env.GetString("servers.1.host") // "b.com"
// If the key doesn't exist but there's a comma-separated base value
// HOSTS=localhost,example.com
host0 := env.GetString("hosts.0") // "localhost" (parsed from comma-separated value)Value Getter Functions
GetString
func GetString(key string, defaultValue ...string) stringGets a string value. Supports dot-path resolution.
Parameters:
key- key name (supports exact match, uppercase conversion, dot path)defaultValue- optional default value
Returns:
string- value or default value (returns empty string when not found and no default)
// Basic usage
host := env.GetString("HOST", "localhost")
// Dot-path access (JSON/YAML nested structures)
dbHost := env.GetString("database.host", "localhost")
appName := env.GetString("app.name")
// Returns empty string when no default
value := env.GetString("NON_EXISTENT") // ""GetInt
func GetInt(key string, defaultValue ...int64) int64Gets an integer value. Automatically converts strings to integers. Supports dot-path resolution.
Parameters:
key- key name (supports dot path)defaultValue- optional default value, typeint64
Returns:
int64- value or default value (returns 0 when not found and no default)
port := env.GetInt("PORT", 8080)
maxConn := env.GetInt("database.max_connections", 10)
// Returns 0 when no default
value := env.GetInt("NON_EXISTENT") // 0GetBool
func GetBool(key string, defaultValue ...bool) boolGets a boolean value. Supports dot-path resolution.
- Truthy values (case-insensitive):
true,1,yes,on,enabled - Falsy values (case-insensitive):
false,0,no,off,disabled
Parameters:
key- key name (supports dot path)defaultValue- optional default value
Returns:
bool- value or default value (returns false when not found and no default)
debug := env.GetBool("DEBUG", false)
cacheEnabled := env.GetBool("cache.enabled", true)
// Returns false when no default
value := env.GetBool("NON_EXISTENT") // falseGetUint64
func GetUint64(key string, defaultValue ...uint64) uint64Gets an unsigned integer value. Supports dot-path resolution.
Parameters:
key- key name (supports dot path)defaultValue- optional default value, typeuint64
Returns:
uint64- value or default value (returns 0 when not found and no default)
port := env.GetUint64("PORT", 8080)
maxSize := env.GetUint64("MAX_SIZE", 1024)
// Returns 0 when no default
value := env.GetUint64("NON_EXISTENT") // 0GetFloat64
func GetFloat64(key string, defaultValue ...float64) float64Gets a floating-point value. Supports dot-path resolution.
Parameters:
key- key name (supports dot path)defaultValue- optional default value, typefloat64
Returns:
float64- value or default value (returns 0 when not found and no default)
rate := env.GetFloat64("RATE", 0.5)
threshold := env.GetFloat64("THRESHOLD")
// Returns 0 when no default
value := env.GetFloat64("NON_EXISTENT") // 0GetDuration
func GetDuration(key string, defaultValue ...time.Duration) time.DurationGets a duration value. Supports dot-path resolution.
Supported formats:
300ms- milliseconds1.5s- seconds2m30s- minutes + seconds1h30m- hours + minutes
Parameters:
key- key name (supports dot path)defaultValue- optional default value
Returns:
time.Duration- value or default value (returns 0 when not found and no default)
timeout := env.GetDuration("TIMEOUT", 30*time.Second)
interval := env.GetDuration("INTERVAL", 5*time.Minute)
// Returns 0 when no default
value := env.GetDuration("NON_EXISTENT") // 0GetSecure
func GetSecure(key string) *SecureValueGets a secure value (for sensitive data).
Parameters:
key- key name
Returns:
*SecureValue- secure value wrapper, returns nil when key doesn't exist or loader unavailable
secret := env.GetSecure("API_KEY")
if secret != nil {
defer secret.Release()
value := secret.Reveal() // plaintext value (call only when needed)
masked := secret.Masked() // for logging: [SECURE:32 bytes]
}WARNING
You must call Release() or Close() after use to release resources. Using defer is recommended to ensure release.
TIP
See SecureValue API for complete API documentation.
GetSlice[T]
func GetSlice[T sliceElement](key string, defaultValue ...[]T) []TGeneric function to get a slice value.
Supported types: string, int, int64, uint, uint64, bool, float64, time.Duration
Note: This is a generic function, not a Loader method. To get a slice from a specific Loader instance, use GetSliceFrom[T].
Parse order:
- First looks up indexed keys
KEY_0,KEY_1,KEY_2... - If no indexed keys, parses the
KEYvalue by comma separation - Supports dot-path resolution
Parameters:
key- key namedefaultValue- optional default value
Returns:
[]T- slice value
// Indexed key format (recommended)
// HOSTS_0=localhost
// HOSTS_1=example.com
hosts := env.GetSlice[string]("HOSTS") // ["localhost", "example.com"]
// Comma-separated format
// PORTS=80,443,8080
ports := env.GetSlice[int64]("PORTS", []int64{80}) // [80, 443, 8080]
// Float slice
rates := env.GetSlice[float64]("RATES", []float64{0.1, 0.2})
// Boolean slice
flags := env.GetSlice[bool]("FLAGS")
// Duration slice
timeouts := env.GetSlice[time.Duration]("TIMEOUTS")
// Unsigned integer slice
ports := env.GetSlice[uint]("PORTS")
port64s := env.GetSlice[uint64]("PORTS")
// int type
portInts := env.GetSlice[int]("PORTS")
// Returns nil when no default
value := env.GetSlice[string]("NON_EXISTENT") // nilGetSliceFrom[T]
func GetSliceFrom[T sliceElement](loader *Loader, key string, defaultValue ...[]T) []TGets a slice value from a specified Loader instance. This is a standalone generic function (not a Loader method).
Parameters:
loader- Loader instance pointer (returns default value if nil)key- key namedefaultValue- optional default value
Returns:
[]T- slice value
Supported types: string, int, int64, uint, uint64, bool, float64, time.Duration
loader, _ := env.New(cfg)
defer loader.Close()
// Get slice from loader instance
hosts := env.GetSliceFrom[string](loader, "HOSTS")
ports := env.GetSliceFrom[int64](loader, "PORTS", []int64{80})
// Also supports int, uint, uint64 types
portsInt := env.GetSliceFrom[int](loader, "PORTS")
portsUint := env.GetSliceFrom[uint](loader, "PORTS")
portsUint64 := env.GetSliceFrom[uint64](loader, "PORTS")TIP
GetSlice[T]- package-level function using the default loaderGetSliceFrom[T]- generic function specifying a Loader instance (Go does not support generic methods)
Query Functions
Lookup
func Lookup(key string) (string, bool)Checks whether a key exists and gets its value. Supports dot-path resolution.
Parameters:
key- key name (supports dot path)
Returns:
string- value (leading/trailing whitespace removed)bool- whether it exists
value, exists := env.Lookup("API_KEY")
if !exists {
// key does not exist
}
// Dot path
if value, exists := env.Lookup("database.host"); exists {
fmt.Println(value)
}Keys
func Keys() []stringGets all key names.
Returns:
[]string- key name list, returns nil when loader unavailable
keys := env.Keys()
for _, key := range keys {
fmt.Println(key)
}All
func All() map[string]stringGets all key-value pairs.
Returns:
map[string]string- key-value mapping, returns nil when loader unavailable
all := env.All()
for key, value := range all {
fmt.Printf("%s=%s\n", key, value)
}Len
func Len() intGets the variable count.
Returns:
int- variable count, returns 0 when loader unavailable
count := env.Len()
fmt.Printf("Loaded %d environment variables\n", count)Set and Delete
Set
func Set(key, value string) errorSets an environment variable.
Parameters:
key- key namevalue- value
Returns:
error- set error
Error types:
*ValidationError- invalid key name format (Field="key")*SecurityError- key is forbidden (matchable witherrors.Is(err, env.ErrSecurityViolation))ErrInvalidValue- invalid value (whenValidateValuesis true, value contains null bytes, control characters, or other unsafe content)ErrClosed- loader has been closed
if err := env.Set("CUSTOM_KEY", "value"); err != nil {
// Could be *SecurityError (forbidden key) or *ValidationError (key format)
}Delete
func Delete(key string) errorDeletes an environment variable.
Parameters:
key- key name
Returns:
error- delete error
if err := env.Delete("TEMP_KEY"); err != nil {
panic(err)
}Validation and Mapping
Validate
func Validate() errorValidates that required keys exist. Requires RequiredKeys to be set in Config.
Returns:
error- validation error
// Need to configure RequiredKeys first (via custom loader)
cfg := env.ProductionConfig()
cfg.RequiredKeys = []string{"DB_HOST", "API_KEY"}
loader, _ := env.New(cfg)
loader.LoadFiles(".env")
if err := loader.Validate(); err != nil {
// Missing required keys
}ParseInto
func ParseInto(v any) errorMaps environment variables to a struct.
Parameters:
v- struct pointer
Returns:
error- mapping error
type Config struct {
Host string `env:"HOST" envDefault:"localhost"`
Port int64 `env:"PORT" envDefault:"8080"`
}
var cfg Config
if err := env.ParseInto(&cfg); err != nil {
panic(err)
}Struct tags:
| Tag | Description |
|---|---|
env:"KEY" | Map to specified key |
env:"-" | Ignore this field |
envDefault:"value" | Default value |
Slice fields are separated by comma , by default (spaces around the separator are automatically removed); there is no custom separator tag.
TIP
See Struct Mapping for the complete guide.
Utility Functions
ResetDefaultLoader
func ResetDefaultLoader() errorResets the global default loader. Primarily used in test scenarios.
Returns:
error- error from closing the old loader (if any); returns nil if there was no loader or closing succeeded
Behavior:
- After locking with
defaultMu.Lock(), atomically swaps the default loader to nil usingdefaultLoader.Swap(nil), then immediately releases the lock - Closes the old loader outside the lock (to avoid potentially time-consuming cleanup while holding the lock, preventing deadlocks if
Close()triggers code that needs the default loader) - After reset, allows creating a new default loader via
Load()orLoadWithConfig()
func TestMain(m *testing.M) {
if err := env.ResetDefaultLoader(); err != nil {
log.Printf("warning: failed to reset loader: %v", err)
}
os.Exit(m.Run())
}
func TestSomething(t *testing.T) {
if err := env.ResetDefaultLoader(); err != nil {
t.Logf("warning: %v", err)
}
defer env.ResetDefaultLoader()
// ... test code
}WARNING
This function is concurrency-safe but should only be called during tests or startup to avoid unexpected behavior.
LoadWithConfig
func LoadWithConfig(cfg Config) errorInitializes the default loader with custom configuration.
Parameters:
cfg- custom configuration
Returns:
error- initialization error
Behavior:
- Sets the package-level default loader (used by
GetString,GetInt, etc.) - Forces
AutoApply = true(regardless of cfg setting) - Returns
ErrAlreadyInitializedif the default loader is already initialized
Difference from Load:
Load()- accepts only a file name list, uses default configLoadWithConfig()- accepts full Config, supports all configuration options
cfg := env.DefaultConfig()
cfg.Filenames = []string{".env.production"}
cfg.OverwriteExisting = true
if err := env.LoadWithConfig(cfg); err != nil {
log.Fatal(err)
}
// Now package-level functions can be used
port := env.GetInt("PORT", 8080)WARNING
This function forces cfg.AutoApply to true, ensuring variables are applied to the system environment. To control application timing, use New() to create an independent instance.
Serialization Functions
Marshal
func Marshal(data any, format ...FileFormat) (string, error)Serializes data to a string in the specified format. Supports map[string]string or struct as input.
Interface integration: If the input type implements the Marshaler interface, the MarshalEnv() method is called first for serialization.
Parameters:
data- data to serialize (map or struct)format- optional format, defaults toFormatEnv
Returns:
string- serialized string (keys sorted)error- serialization error
Supported formats:
FormatEnv(default) - .env formatFormatJSON- JSON formatFormatYAML- YAML format
// map to .env format
mapData := map[string]string{"HOST": "localhost", "PORT": "8080"}
envStr, _ := env.Marshal(mapData)
// HOST=localhost
// PORT=8080
// map to JSON format (numeric strings output as numbers, keys sorted alphabetically)
jsonStr, _ := env.Marshal(mapData, env.FormatJSON)
// {
// "HOST": "localhost",
// "PORT": 8080
// }
// struct to .env format
type Config struct {
Host string `env:"HOST"`
Port string `env:"PORT"`
}
envStr, _ := env.Marshal(Config{Host: "localhost", Port: "8080"})UnmarshalMap
func UnmarshalMap(data string, format ...FileFormat) (map[string]string, error)Parses a formatted string into a map. Supports auto format detection.
Parameters:
data- formatted stringformat- optional format, defaults toFormatEnv; useFormatAutofor auto-detection
Returns:
map[string]string- parsed key-value pairserror- parse error
// .env format
m, _ := env.UnmarshalMap("HOST=localhost\nPORT=8080")
// JSON format (nested structures are flattened)
m, _ := env.UnmarshalMap(`{"database": {"host": "localhost"}}`, env.FormatJSON)
// m["DATABASE_HOST"] = "localhost"
// Auto-detect format
m, _ := env.UnmarshalMap(jsonString, env.FormatAuto)UnmarshalStruct
func UnmarshalStruct(data string, v any, format ...FileFormat) errorParses a formatted string and fills a struct.
Parameters:
data- formatted stringv- struct pointerformat- optional format, defaults toFormatEnv
Returns:
error- parse error
type Config struct {
Host string `env:"SERVER_HOST"`
Port int `env:"SERVER_PORT"`
}
var cfg Config
err := env.UnmarshalStruct("SERVER_HOST=localhost\nSERVER_PORT=8080", &cfg)
// cfg.Host = "localhost", cfg.Port = 8080
// Parse from JSON
err = env.UnmarshalStruct(`{"server": {"host": "localhost"}}`, &cfg, env.FormatJSON)UnmarshalInto
func UnmarshalInto(data map[string]string, v any) errorFills a struct from a map. Supports env and envDefault tags.
Interface integration: If the target type implements the Unmarshaler interface, the UnmarshalEnv(data) method is called first.
Parameters:
data- key-value mappingv- struct pointer
Returns:
error- fill error
type Config struct {
Host string `env:"HOST" envDefault:"localhost"`
Port int `env:"PORT" envDefault:"8080"`
}
data := map[string]string{"HOST": "example.com"}
var cfg Config
err := env.UnmarshalInto(data, &cfg)
// cfg.Host = "example.com", cfg.Port = 8080 (uses default)MarshalStruct
func MarshalStruct(v any) (map[string]string, error)Converts a struct to a map. Supports env tags for key names.
Interface integration: If the input type implements the Marshaler interface, the MarshalEnv() method is called first.
Parameters:
v- struct or struct pointer
Returns:
map[string]string- key-value mappingerror- conversion error
type Config struct {
Host string `env:"SERVER_HOST"`
Port int `env:"SERVER_PORT"`
}
cfg := Config{Host: "localhost", Port: 8080}
m, _ := env.MarshalStruct(cfg)
// m["SERVER_HOST"] = "localhost"
// m["SERVER_PORT"] = "8080"IsMarshalError
func IsMarshalError(err error) boolChecks whether an error is a serialization/deserialization error.
Parameters:
err- error to check
Returns:
bool- whether it's a MarshalError type
_, err := env.MarshalStruct(invalidData)
if env.IsMarshalError(err) {
// Handle serialization error
}Complete Example
package main
import (
"fmt"
"log"
"time"
"github.com/cybergodev/env"
)
type AppConfig struct {
Host string `env:"APP_HOST" envDefault:"0.0.0.0"`
Port int64 `env:"APP_PORT" envDefault:"8080"`
Debug bool `env:"DEBUG" envDefault:"false"`
Timeout time.Duration `env:"TIMEOUT" envDefault:"30s"`
Hosts []string `env:"HOSTS"`
}
func main() {
// Load configuration files
if err := env.Load(".env"); err != nil {
log.Printf("Warning: %v", err)
}
// Read individual values
host := env.GetString("APP_HOST", "localhost")
port := env.GetInt("APP_PORT", 8080)
debug := env.GetBool("DEBUG", false)
timeout := env.GetDuration("TIMEOUT", 30*time.Second)
fmt.Printf("Server: %s:%d\n", host, port)
fmt.Printf("Debug: %v, Timeout: %v\n", debug, timeout)
// Sensitive data
secret := env.GetSecure("API_KEY")
if secret != nil {
defer secret.Release()
fmt.Printf("API Key length: %d\n", secret.Length())
}
// Struct mapping
var cfg AppConfig
if err := env.ParseInto(&cfg); err != nil {
log.Fatal(err)
}
fmt.Printf("Config: %+v\n", cfg)
// All variables
fmt.Printf("Loaded %d variables\n", env.Len())
}Related Documentation
- Loader API - Loader instance methods
- Config API - Configuration options
- SecureValue API - Secure value handling
- Struct Mapping - Struct mapping guide