env

package module
v11.2.2 Latest Latest
Warning

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

Go to latest
Published: Aug 7, 2024 License: MIT Imports: 9 Imported by: 295

README

GoReleaser Logo

A simple, zero-dependencies library to parse environment variables into structs.

Getting started

type config struct {
  Home string `env:"HOME"`
}

// parse
var cfg config
err := env.Parse(&cfg)

// parse with generics
cfg, err := env.ParseAs[config]()

You can see the full documentation and list of examples at pkg.go.dev.


Used and supported by

encore icon

Encore – the platform for building Go-based cloud backends.

Usage Caveats

[!CAUTION]

Unexported fields will be ignored by env. This is by design and will not change.

Functions
  • Parse: parse the current environment into a type
  • ParseAs: parse the current environment into a type using generics
  • ParseWithOptions: parse the current environment into a type with custom options
  • ParseAsithOptions: parse the current environment into a type with custom options and using generics
  • Must: can be used to wrap Parse.* calls to panic on error
  • GetFieldParams: get the env parsed options for a type
  • GetFieldParamsWithOptions: get the env parsed options for a type with custom options
Supported types

Out of the box all built-in types are supported, plus a few others that are commonly used.

Complete list:

  • bool
  • float32
  • float64
  • int16
  • int32
  • int64
  • int8
  • int
  • string
  • uint16
  • uint32
  • uint64
  • uint8
  • uint
  • time.Duration
  • encoding.TextUnmarshaler
  • url.URL

Pointers, slices and slices of pointers, and maps of those types are also supported.

You may also add custom parsers for your types.

Tags

The following tags are provided:

  • env: sets the environment variable name and optionally takes the tag options described below
  • envDefault: sets the default value for the field
  • envPrefix: can be used in a field that is a complex type to set a prefix to all environment variables used in it
  • envSeparator: sets the character to be used to separate items in slices and maps (default: ,)
  • envKeyValSeparator: sets the character to be used to separate keys and their values in maps (default: :)
env tag options

Here are all the options available for the env tag:

  • ,expand: expands environment variables, e.g. FOO_${BAR}
  • ,file: instructs that the content of the variable is a path to a file that should be read
  • ,init: initialize nil pointers
  • ,notEmpty: make the field errors if the environment variable is empty
  • ,required: make the field errors if the environment variable is not set
  • ,unset: unset the environment variable after use
Parse Options

There are a few options available in the functions that end with WithOptions:

  • Environment: keys and values to be used instead of os.Environ()
  • TagName: specifies another tag name to use rather than the default env
  • RequiredIfNoDef: set all env fields as required if they do not declare envDefault
  • OnSet: allows to hook into the env parsing and do something when a value is set
  • Prefix: prefix to be used in all environment variables
  • UseFieldNameByDefault: defines whether or not env should use the field name by default if the env key is missing
  • FuncMap: custom parse functions for custom types
Documentation and examples

Examples are live in pkg.go.dev, and also in the example test file.

Badges

Release Software License Build status Codecov branch Go docs Conventional Commits

Related projects
  • envdoc - generate documentation for environment variables from env tags
Stargazers over time

Stargazers over time

Documentation

Overview

Package env is a simple, zero-dependencies library to parse environment variables into structs.

Example:

type config struct {
	Home string `env:"HOME"`
}
// parse
var cfg config
err := env.Parse(&cfg)
// or parse with generics
cfg, err := env.ParseAs[config]()

Check the examples and README for more detailed usage.

Example

Basic package usage example.

type Config struct {
	Foo string `env:"FOO"`
}

os.Setenv("FOO", "bar")

// parse:
var cfg1 Config
_ = Parse(&cfg1)

// parse with generics:
cfg2, _ := ParseAs[Config]()

fmt.Print(cfg1.Foo, cfg2.Foo)
Output:

barbar

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Must

func Must[T any](t T, err error) T

Must panic is if err is not nil, and returns t otherwise.

func Parse

func Parse(v interface{}) error

Parse parses a struct containing `env` tags and loads its values from environment variables.

Example

Parse the environment into a struct.

type Config struct {
	Home string `env:"HOME"`
}
os.Setenv("HOME", "/tmp/fakehome")
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Home:/tmp/fakehome}
Example (ComplexSlices)

Handling slices of complex types.

type Test struct {
	Str string `env:"STR"`
	Num int    `env:"NUM"`
}
type Config struct {
	Foo []Test `envPrefix:"FOO"`
}

os.Setenv("FOO_0_STR", "a")
os.Setenv("FOO_0_NUM", "1")
os.Setenv("FOO_1_STR", "b")
os.Setenv("FOO_1_NUM", "2")

var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v\n", cfg)
Output:

{Foo:[{Str:a Num:1} {Str:b Num:2}]}
Example (CustomTimeFormat)

By default, env supports anything that implements the `TextUnmarshaler` interface, which includes `time.Time`.

The upside is that depending on the format you need, you don't need to change anything.

The downside is that if you do need time in another format, you'll need to create your own type and implement `TextUnmarshaler`.

// type MyTime time.Time
//
// func (t *MyTime) UnmarshalText(text []byte) error {
// 	tt, err := time.Parse("2006-01-02", string(text))
// 	*t = MyTime(tt)
// 	return err
// }

type Config struct {
	SomeTime MyTime `env:"SOME_TIME"`
}
os.Setenv("SOME_TIME", "2021-05-06")
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Print(cfg.SomeTime)
Output:

{0 63755856000 <nil>}
Example (ErrorHandling)
type Config struct {
	Username string `env:"EX_ERR_USERNAME" envDefault:"admin"`
	Password string `env:"EX_ERR_PASSWORD,notEmpty"`
}

var cfg Config
if err := Parse(&cfg); err != nil {
	if errors.Is(err, EmptyEnvVarError{}) {
		fmt.Println("oopsie")
	}
	aggErr := AggregateError{}
	if ok := errors.As(err, &aggErr); ok {
		for _, er := range aggErr.Errors {
			switch v := er.(type) {
			// Handle the error types you need:
			// ParseError
			// NotStructPtrError
			// NoParserError
			// NoSupportedTagOptionError
			// EnvVarIsNotSetError
			// EmptyEnvVarError
			// LoadFileContentError
			// ParseValueError
			case EmptyEnvVarError:
				fmt.Println("daisy")
			default:
				fmt.Printf("Unknown error type %v", v)
			}
		}
	}
}

fmt.Printf("%+v", cfg)
Output:

oopsie
daisy
{Username:admin Password:}
Example (Expand)

If you set the `expand` option, environment variables (either in `${var}` or `$var` format) in the string will be replaced according with the actual value of the variable. For example:

type Config struct {
	Expand1 string `env:"EXPAND_1,expand"`
	Expand2 string `env:"EXPAND_2,expand" envDefault:"ABC_${EXPAND_1}"`
}
os.Setenv("EXPANDING", "HI")
os.Setenv("EXPAND_1", "HELLO_${EXPANDING}")
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Expand1:HELLO_HI Expand2:ABC_HELLO_HI}
Example (FromFile)

The `env` tag option `file` (e.g., `env:"tagKey,file"`) can be added in order to indicate that the value of the variable shall be loaded from a file.

The path of that file is given by the environment variable associated with it.

f, _ := os.CreateTemp("", "")
_, _ = io.WriteString(f, "super secret")
_ = f.Close()

type Config struct {
	Secret string `env:"SECRET,file"`
}
os.Setenv("SECRET", f.Name())
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Secret:super secret}
Example (Init)

You can automatically initialize `nil` pointers regardless of if a variable is set for them or not. This behavior can be enabled by using the `init` tag option.

type Inner struct {
	A string `env:"OLA" envDefault:"HI"`
}
type Config struct {
	NilInner  *Inner
	InitInner *Inner `env:",init"`
}
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Print(cfg.NilInner, cfg.InitInner)
Output:

<nil> &{HI}
Example (NotEmpty)

While `required` demands the environment variable to be set, it doesn't check its value. If you want to make sure the environment is set and not empty, you need to use the `notEmpty` tag option instead (`env:"SOME_ENV,notEmpty"`).

type Config struct {
	Nope string `env:"NOPE,notEmpty"`
}
os.Setenv("NOPE", "")
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

env: environment variable "NOPE" should not be empty
{Nope:}
Example (Prefix)

Setting prefixes for inner types.

type Inner struct {
	Foo string `env:"FOO,required"`
}
type Config struct {
	A Inner `envPrefix:"A_"`
	B Inner `envPrefix:"B_"`
}
os.Setenv("A_FOO", "a")
os.Setenv("B_FOO", "b")
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{A:{Foo:a} B:{Foo:b}}
Example (Required)
type Config struct {
	Nope string `env:"NOPE,required"`
}
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

env: required environment variable "NOPE" is not set
{Nope:}
Example (Separator)

You can use `envSeparator` to define which character should be used to separate array items in a string. Similarly, you can use `envKeyValSeparator` to define which character should be used to separate a key from a value in a map. The defaults are `,` and `:`, respectively.

type Config struct {
	Map map[string]string `env:"CUSTOM_MAP" envSeparator:"-" envKeyValSeparator:"|"`
}
os.Setenv("CUSTOM_MAP", "k1|v1-k2|v2")
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Map:map[k1:v1 k2:v2]}
Example (SetDefaults)

You can define the default value for a field by either using the `envDefault` tag, or when initializing the `struct`.

Default values defined as `struct` tags will overwrite existing values during `Parse`.

type Config struct {
	Foo string `env:"DEF_FOO"`
	Bar string `env:"DEF_BAR" envDefault:"bar"`
}
cfg := Config{
	Foo: "foo",
}
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Foo:foo Bar:bar}
Example (Unset)

The `env` tag option `unset` (e.g., `env:"tagKey,unset"`) can be added to ensure that some environment variable is unset after reading it.

type Config struct {
	Secret string `env:"SECRET,unset"`
}
os.Setenv("SECRET", "1234")
var cfg Config
if err := Parse(&cfg); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v - %s", cfg, os.Getenv("SECRET"))
Output:

{Secret:1234} -

func ParseAs

func ParseAs[T any]() (T, error)

ParseAs parses the given struct type containing `env` tags and loads its values from environment variables.

Example

Parse the environment into a struct using generics.

type Config struct {
	Home string `env:"HOME"`
}
os.Setenv("HOME", "/tmp/fakehome")
cfg, err := ParseAs[Config]()
if err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Home:/tmp/fakehome}

func ParseAsWithOptions

func ParseAsWithOptions[T any](opts Options) (T, error)

ParseWithOptions parses the given struct type containing `env` tags and loads its values from environment variables.

func ParseWithOptions

func ParseWithOptions(v interface{}, opts Options) error

ParseWithOptions parses a struct containing `env` tags and loads its values from environment variables.

Example (AllFieldsRequired)

Make all fields required by default.

type Config struct {
	Username string `env:"EX_USERNAME" envDefault:"admin"`
	Password string `env:"EX_PASSWORD"`
}

var cfg Config
if err := ParseWithOptions(&cfg, Options{
	RequiredIfNoDef: true,
}); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v\n", cfg)
Output:

env: required environment variable "EX_PASSWORD" is not set
{Username:admin Password:}
Example (CustomTypes)

Parse using extra options.

type Thing struct {
	desc string
}

type Config struct {
	Thing Thing `env:"THING"`
}

os.Setenv("THING", "my thing")

c := Config{}
err := ParseWithOptions(&c, Options{
	FuncMap: map[reflect.Type]ParserFunc{
		reflect.TypeOf(Thing{}): func(v string) (interface{}, error) {
			return Thing{desc: v}, nil
		},
	},
})
if err != nil {
	fmt.Println(err)
}
fmt.Print(c.Thing.desc)
Output:

my thing
Example (OnSet)

You might want to listen to value sets and, for example, log something or do some other kind of logic.

type config struct {
	Home         string `env:"HOME,required"`
	Port         int    `env:"PORT" envDefault:"3000"`
	IsProduction bool   `env:"PRODUCTION"`
	NoEnvTag     bool
	Inner        struct{} `envPrefix:"INNER_"`
}
os.Setenv("HOME", "/tmp/fakehome")
var cfg config
if err := ParseWithOptions(&cfg, Options{
	OnSet: func(tag string, value interface{}, isDefault bool) {
		fmt.Printf("Set %s to %v (default? %v)\n", tag, value, isDefault)
	},
}); err != nil {
	fmt.Println("failed:", err)
}
fmt.Printf("%+v", cfg)
Output:

Set HOME to /tmp/fakehome (default? false)
Set PORT to 3000 (default? true)
Set PRODUCTION to  (default? false)
{Home:/tmp/fakehome Port:3000 IsProduction:false NoEnvTag:false Inner:{}}
Example (Prefix)

Setting prefixes for the entire config.

type Config struct {
	Foo string `env:"FOO"`
}
os.Setenv("MY_APP_FOO", "a")
var cfg Config
if err := ParseWithOptions(&cfg, Options{
	Prefix: "MY_APP_",
}); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Foo:a}
Example (SetEnv)

Set a custom environment. By default, `os.Environ()` is used.

type Config struct {
	Username string `env:"EX_USERNAME" envDefault:"admin"`
	Password string `env:"EX_PASSWORD"`
}

var cfg Config
if err := ParseWithOptions(&cfg, Options{
	Environment: map[string]string{
		"EX_USERNAME": "john",
		"EX_PASSWORD": "cena",
	},
}); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v\n", cfg)
Output:

{Username:john Password:cena}
Example (TagName)

Use a different tag name than `env`.

type Config struct {
	Home string `json:"HOME"`
}
os.Setenv("HOME", "hello")
var cfg Config
if err := ParseWithOptions(&cfg, Options{
	TagName: "json",
}); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Home:hello}
Example (UseFieldName)

If you don't want to set the `env` tag on every field, you can use the `UseFieldNameByDefault` option.

It will use the field name to define the environment variable name. So, `Foo` becomes `FOO`, `FooBar` becomes `FOO_BAR`, and so on.

type Config struct {
	Foo string
}
os.Setenv("FOO", "bar")
var cfg Config
if err := ParseWithOptions(&cfg, Options{
	UseFieldNameByDefault: true,
}); err != nil {
	fmt.Println(err)
}
fmt.Printf("%+v", cfg)
Output:

{Foo:bar}

func ToMap

func ToMap(env []string) map[string]string

ToMap Converts list of env vars as provided by os.Environ() to map you can use as Options.Environment field

Types

type AggregateError

type AggregateError struct {
	Errors []error
}

An aggregated error wrapper to combine gathered errors. This allows either to display all errors or convert them individually List of the available errors ParseError NotStructPtrError NoParserError NoSupportedTagOptionError EnvVarIsNotSetError EmptyEnvVarError LoadFileContentError ParseValueError

func (AggregateError) Error

func (e AggregateError) Error() string

func (AggregateError) Is

func (e AggregateError) Is(err error) bool

Is conforms with errors.Is.

type EmptyEnvVarError

type EmptyEnvVarError struct {
	Key string
}

This error occurs when the variable which must be not empty is existing but has an empty value Read about not empty fields: https://github.com/caarlos0/env#not-empty-fields

func (EmptyEnvVarError) Error

func (e EmptyEnvVarError) Error() string

type EnvVarIsNotSetError

type EnvVarIsNotSetError struct {
	Key string
}

This error occurs when the required variable is not set Read about required fields: https://github.com/caarlos0/env#required-fields

func (EnvVarIsNotSetError) Error

func (e EnvVarIsNotSetError) Error() string

type FieldParams

type FieldParams struct {
	OwnKey          string
	Key             string
	DefaultValue    string
	HasDefaultValue bool
	Required        bool
	LoadFile        bool
	Unset           bool
	NotEmpty        bool
	Expand          bool
	Init            bool
}

FieldParams contains information about parsed field tags.

func GetFieldParams

func GetFieldParams(v interface{}) ([]FieldParams, error)

GetFieldParams parses a struct containing `env` tags and returns information about tags it found.

func GetFieldParamsWithOptions

func GetFieldParamsWithOptions(v interface{}, opts Options) ([]FieldParams, error)

GetFieldParamsWithOptions parses a struct containing `env` tags and returns information about tags it found.

type LoadFileContentError

type LoadFileContentError struct {
	Filename string
	Key      string
	Err      error
}

This error occurs when it's impossible to load the value from the file Read about From file feature: https://github.com/caarlos0/env#from-file

func (LoadFileContentError) Error

func (e LoadFileContentError) Error() string

type NoParserError

type NoParserError struct {
	Name string
	Type reflect.Type
}

This error occurs when there is no parser provided for given type Supported types and defaults: https://github.com/caarlos0/env#supported-types-and-defaults How to create a custom parser: https://github.com/caarlos0/env#custom-parser-funcs

func (NoParserError) Error

func (e NoParserError) Error() string

type NoSupportedTagOptionError

type NoSupportedTagOptionError struct {
	Tag string
}

This error occurs when the given tag is not supported In-built supported tags: "", "file", "required", "unset", "notEmpty", "expand", "envDefault", "envSeparator" How to create a custom tag: https://github.com/caarlos0/env#changing-default-tag-name

func (NoSupportedTagOptionError) Error

type NotStructPtrError

type NotStructPtrError struct{}

The error occurs when pass something that is not a pointer to a Struct to Parse

func (NotStructPtrError) Error

func (e NotStructPtrError) Error() string

type OnSetFn

type OnSetFn func(tag string, value interface{}, isDefault bool)

OnSetFn is a hook that can be run when a value is set.

type Options

type Options struct {
	// Environment keys and values that will be accessible for the service.
	Environment map[string]string

	// TagName specifies another tag name to use rather than the default 'env'.
	TagName string

	// RequiredIfNoDef automatically sets all fields as required if they do not
	// declare 'envDefault'.
	RequiredIfNoDef bool

	// OnSet allows to run a function when a value is set.
	OnSet OnSetFn

	// Prefix define a prefix for every key.
	Prefix string

	// UseFieldNameByDefault defines whether or not `env` should use the field
	// name by default if the `env` key is missing.
	// Note that the field name will be "converted" to conform with environment
	// variable names conventions.
	UseFieldNameByDefault bool

	// Custom parse functions for different types.
	FuncMap map[reflect.Type]ParserFunc
	// contains filtered or unexported fields
}

Options for the parser.

type ParseError

type ParseError struct {
	Name string
	Type reflect.Type
	Err  error
}

The error occurs when it's impossible to convert the value for given type.

func (ParseError) Error

func (e ParseError) Error() string

type ParseValueError

type ParseValueError struct {
	Msg string
	Err error
}

This error occurs when it's impossible to convert value using given parser Supported types and defaults: https://github.com/caarlos0/env#supported-types-and-defaults How to create a custom parser: https://github.com/caarlos0/env#custom-parser-funcs

func (ParseValueError) Error

func (e ParseValueError) Error() string

type ParserFunc

type ParserFunc func(v string) (interface{}, error)

ParserFunc defines the signature of a function that can be used within `Options`' `FuncMap`.

Jump to

Keyboard shortcuts

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