envdecode

repository·master·Indexed 42 days ago

https://github.com/joeshaw/envdecode

A Go package to populate structs from environment variables using `env` struct tags. It supports nested structures, default values, required fields, and custom decoding logic via the Decoder interface. Features include standard decoding with Decode(), fail-fast validation with StrictDecode(), and fatal error handling with MustDecode() and MustStrictDecode(). It supports primitives, slices (semicolon-delimited), time.Duration, *url.URL, and provides an Export() function to retrieve configuration metadata.

Tokens
2.2K
Snippets
9
Records
12
Agent score
4%

What's inside envdecode

  1. Populate structs from environment variables with envdecode

    master

    Use envdecode to map environment variables to Go struct fields using env struct tags. The package supports nested structs and pointers to nested structs, but it will not allocate new pointers to structs for you.

    Requirements:

    • Fields must be exported (start with a capital letter).
    • Structs with no exported fields or no env tags will return an error during decoding.
    type Config struct {
        Hostname  string `env:"SERVER_HOSTNAME,default=localhost"`
        Port      uint16 `env:"SERVER_PORT,default=8080"` 
    }
    
    var cfg Config
    err := envdecode.Decode(&cfg)
  2. Implement a custom Decoder interface

    master

    To provide custom decoding logic for a specific field, implement the Decoder interface for that type. The Decode method receives the string representation of the environment variable.

    type Config struct {
      IPAddr IP `env:"IP_ADDR"`
    }
    
    type IP net.IP
    
    // Decode implements the interface `envdecode.Decoder`
    func (i *IP) Decode(repl string) error {
      *i = net.ParseIP(repl)
      return nil
    }
  3. Use Decode and StrictDecode

    master

    There are two primary ways to decode environment variables into a struct:

    1. envdecode.Decode(&cfg): Standard decoding mode.
    2. envdecode.StrictDecode(&cfg): A mode where all fields act as strict. All parse errors will fail fast and return an error immediately.
    var cfg Config
    
    // Standard decode
    err := envdecode.Decode(&cfg)
    
    // Strict decode (all fields fail fast on parse errors)
    err := envdecode.StrictDecode(&cfg)
  4. Configure env struct tags (default, required, strict)

    master

    You can control how environment variables are parsed by appending options to the env struct tag, separated by commas:

    • default=value: Provides a fallback value if the environment variable is not set.
    • required: Returns an error if the environment variable is not present.
    • strict: Returns an error if there is an error while parsing the value (useful for specific field validation).
    type Config struct {
        Hostname  string        `env:"SERVER_HOSTNAME,default=localhost"` // Default value
        Secret    string        `env:"AWS_SECRET_ACCESS_KEY,required"`    // Must be present
        Timeout   time.Duration `env:"TIMEOUT,default=1m,strict"`         // Strict parsing
    }
  5. Supported types in envdecode

    master

    The following types are supported for environment variable mapping:

    • Structs: Including pointers to structs.
    • Slices: Slices of any supported type, where values in the environment variable are separated by a semicolon (;).
    • Primitives: bool, float32, float64, int (and all sized variants int8 through int64), uint (and all sized variants uint8 through uint64), and string.
    • Specialized Types:
      • time.Duration: Uses time.ParseDuration() format.
      • *url.URL: Uses url.Parse().
    • Custom Types: Any type that implements the Decoder interface.
  6. Decode environment variables into structs with Decode()

    master

    Use Decode(target) to populate a struct from environment variables. The target must be a non-nil pointer to a struct. Fields must be exported and tagged with env.

    Supported tag options:

    • env:"VAR_NAME": The name of the environment variable.
    • env:"VAR_NAME,default=value": Provides a default value if the environment variable is empty.
    • env:"VAR_NAME,required": Returns an error if the environment variable is empty. Note: You cannot use default and required together.
    • env:"VAR_NAME,strict": Returns an error if there is a parsing error for that specific field.

    Supported types include all primitives (bool, int, float, string), time.Duration, *url.URL, and slices of these types (using ; as a delimiter for slice elements). Structs and pointers to structs are decoded recursively.

    type Config struct {
    	Port    int           `env:"PORT,default=8080"` 
    	Timeout time.Duration `env:"TIMEOUT,default=30s"` 
    	URL     *url.URL      `env:"API_URL,required"` 
    	Tags    []string      `env:"TAGS"` // Decoded from "tag1;tag2;tag3"
    }
    
    var cfg Config
    err := envdecode.Decode(&cfg)
    if err != nil {
    	// handle error
    }
  7. Use StrictDecode() for mandatory parsing validation

    master

    If you want every field in your struct to behave as if it has the ,strict tag (returning an error on any parsing failure), use StrictDecode(target). This is useful for ensuring that environment variables are not just present, but also correctly formatted according to their type.

    err := envdecode.StrictDecode(&cfg)
  8. Use MustDecode() and MustStrictDecode() for fatal errors

    master

    If you want your application to terminate immediately if environment variables cannot be decoded (e.g., during startup), use MustDecode(target) or MustStrictDecode(target).

    By default, these functions use the FailureFunc variable to log the error and call log.Fatalf. You can override FailureFunc with your own implementation to change how the application handles fatal decoding errors (e.g., to load a fallback config).

    // Default behavior: logs and exits
    envdecode.MustDecode(&cfg)
    
    // Customizing failure behavior
    envdecode.FailureFunc = func(err error) {
    	fmt.Printf("Custom error handling: %v\n", err)
    	os.Exit(1)
    }
  9. Export configuration metadata with Export()

    master

    The Export(target) function returns a sorted list of *ConfigInfo objects describing the configuration structure. This is useful for generating documentation, debugging current settings, or inspecting what environment variables your application expects.

    It recursively traverses nested structs and returns metadata including the field name, environment variable name, whether it's required, and the current value (if set).

    configs, err := envdecode.Export(&cfg)
    if err != nil {
    	// handle error
    }
    for _, c := range configs {
    	fmt.Printf("Field: %s, Env: %s, Value: %s\n", c.Field, c.EnvVar, c.Value)
    }
  10. Reference: Error variables

    master

    The following error variables are exported:

    • ErrInvalidTarget: Returned when the target passed to Decode is not a non-nil pointer to a struct.
    • ErrNoTargetFieldsAreSet: Returned when Decode completes but no exported fields with valid env tags were found/set.
  11. Reference: ConfigInfo struct fields

    master

    The ConfigInfo struct returned by Export() contains the following metadata for each tagged field:

    • Field: The name of the struct field (supports dot notation for nested structs, e.g., Parent.Child).
    • EnvVar: The name of the environment variable defined in the env tag.
    • Value: The current string representation of the field's value.
    • DefaultValue: The default value specified in the env tag (if any).
    • HasDefault: Boolean indicating if a default= option was provided.
    • Required: Boolean indicating if the required option was provided.
    • UsesEnv: Boolean indicating if the environment variable is currently set in the environment.
  12. Implement the Decoder interface for custom types

    master

    You can provide custom decoding logic for specific types by implementing the Decoder interface. When envdecode encounters a field that implements Decoder, it will call its Decode(string) error method instead of using default primitive parsing.

    type MyCustomType struct {
    	Value string
    }
    
    func (m *MyCustomType) Decode(val string) error {
    	// Custom logic here
    	m.Value = val
    	return nil
    }
    
    type Config struct {
    	Custom MyCustomType `env:"CUSTOM_VAR"` 
    }