This repo is a heavily modified version of env by https://github.com/caarlos0 . Also major portions we adapted from github.com/joho/godotenv.
This repo provides some simple utilities for dealing with environment variables. There are some basic functions that are simple wrappers to the "os" package's env functions. These include:
err = env.Set("KEY", "secret_key")
err = env.Unset("KEY")
value = env.Get("OTHER_KEY")
value = env.GetOr("OTHER_KEY", "value returned if OTHER_KEY does not exist")
value = env.MustGet("KEY") // panics if KEY does not exist
A much more powerful approach is to populate an annotated struct with values from the environment.
Take the following example:
type ClientConfig struct {
Endpoint []string `env:"ENDPOINT" required:"true"`
HealthCheck bool `env:"HEALTH_CHECK" envDefault:"true"`
HealthCheckTimeout time.Duration `env:"HEALTH_CHECK_TIMEOUT" envDefault:"1s"`
}
This anotated struct can then be populated with the appropiate environment variables but using the following command:
cfg := ClientConfig{}
err := env.Parse(&cfg)
If the struct you are populating is part of a library you may want to have parse the same struct but with different values. You can do this by using a prefix when parsing.
cfg := ClientConfig{}
err := env.ParseWithPrefix(&cfg, "CLIENT2_")
In this case the parser would look for the environment variables CLIENT2_ENDPOINT, CLIENT2_HEALTH_CHECK, etc.
Note: Prefixes must end with an underscore (_). If you provide a prefix without a trailing underscore, Parse will return an error.
The environment variables are Parsed to go into the appropiate types (or an err is thrown if it can not do so). This sames you from writing a some basic validations on envirnment varaibles.
The library has built-in support for the following types:
stringintuintint64boolfloat32float64time.Durationurl.URL[]string[]int[]bool[]float32[]float64[]time.Duration[]url.URL
The required tag will cause an error if the environment variable does not exist:
`env:"ENDPOINT" required:"true"`
The envDefault tag will allow you to provide a default value to use if the environment variable does not exist:
`env:"HEALTH_CHECK" envDefault:"true"`
When assigning to a slice type the "," is used to seperate fields. You can override this with the envSeparator:":" tag to use some other character.
Use MustGetWithContext for better error messages when required variables are missing:
apiBase := env.MustGetWithContext("WALLET_TRANSACTION_API", "generating wallet URIs")
// Panics with: "required env var WALLET_TRANSACTION_API missing (needed for: generating wallet URIs)"The package provides convenient functions for managing environment variables in tests:
func TestConfig(t *testing.T) {
// Automatically restores or removes the variable after the test
env.SetForTest(t, "WALLET_TRANSACTION_API", "https://test.com")
// Test code here
// Cleanup happens automatically via t.Cleanup()
}
func TestWithoutEnv(t *testing.T) {
// Temporarily removes a variable and restores it after test
env.UnsetForTest(t, "OPTIONAL_CONFIG")
// Test code that expects variable to not exist
}Validate that all required environment variables are set before parsing:
// Check required vars without parsing values
if err := env.ValidateRequired(&Config{}, ""); err != nil {
log.Fatalf("Missing required configuration: %v", err)
}
// Now safe to parse
cfg := &Config{}
env.Parse(&cfg)Get information about all environment variables that a struct uses:
// Get all variables (required and optional)
vars, _ := env.GetAllVars(&Config{}, "")
for _, v := range vars {
fmt.Printf("%s: %s (required: %v, default: %s)\n",
v.Name, v.Type, v.Required, v.Default)
}
// Get only required variables
required, _ := env.GetRequiredVars(&Config{}, "")
fmt.Println("Required vars:", required)This is useful for:
- Generating documentation
- Creating example .env files
- Debugging configuration issues
- Validating deployment environments
Enable debug logging to see what environment variables are being read:
// Enable debug logging
env.EnableDebugLogging(log.Printf)
cfg := &Config{}
env.Parse(&cfg)
// Logs: "env: DB_CONNECTION = postgres://... (field: Config.Database)"
// Disable debug logging
env.EnableDebugLogging(nil)This is particularly useful for:
- Debugging configuration issues in production
- Understanding what values are being loaded
- Troubleshooting environment variable naming
Parse errors now include field context for easier debugging:
type Config struct {
Connection string `env:"DB_CONNECTION" required:"true"`
}
err := env.Parse(&Config{})
// Error: "field 'Connection' in Config: env var DB_CONNECTION was missing and is required"