Simple lib to parse environment variables to structs

env

Build Status Coverage Status

Simple lib to parse envs to structs in Go.

Example

A very basic example:

package main

import (
	"fmt"
	"time"

	// if using go modules
	"github.com/caarlos0/env/v6"

	// if using dep/others
	"github.com/caarlos0/env"
)

type config struct {
	Home         string        `env:"HOME"`
	Port         int           `env:"PORT" envDefault:"3000"`
	IsProduction bool          `env:"PRODUCTION"`
	Hosts        []string      `env:"HOSTS" envSeparator:":"`
	Duration     time.Duration `env:"DURATION"`
	TempFolder   string        `env:"TEMP_FOLDER" envDefault:"${HOME}/tmp" envExpand:"true"`
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		fmt.Printf("%+v\n", err)
	}

	fmt.Printf("%+v\n", cfg)
}

You can run it like this:

$ PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run main.go
{Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s}

Supported types and defaults

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

Complete list:

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

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

You can also use/define a custom parser func for any other type you want.

If you set the envDefault tag for something, this value will be used in the case of absence of it in the environment.

By default, slice types will split the environment value on ,; you can change this behavior by setting the envSeparator tag.

If you set the envExpand tag, environment variables (either in ${var} or $var format) in the string will be replaced according with the actual value of the variable.

Unexported fields are ignored.

Custom Parser Funcs

If you have a type that is not supported out of the box by the lib, you are able to use (or define) and pass custom parsers (and their associated reflect.Type) to the env.ParseWithFuncs() function.

In addition to accepting a struct pointer (same as Parse()), this function also accepts a map[reflect.Type]env.ParserFunc.

env also ships with some pre-built custom parser funcs for common types. You can check them out here.

If you add a custom parser for, say Foo, it will also be used to parse *Foo and []Foo types.

This directory contains pre-built, custom parsers that can be used with env.ParseWithFuncs to facilitate the parsing of envs that are not basic types.

Check the example in the go doc for more info.

Required fields

The env tag option required (e.g., env:"tagKey,required") can be added to ensure that some environment variable is set. In the example above, an error is returned if the config struct is changed to:

type config struct {
    Home         string   `env:"HOME"`
    Port         int      `env:"PORT" envDefault:"3000"`
    IsProduction bool     `env:"PRODUCTION"`
    Hosts        []string `env:"HOSTS" envSeparator:":"`
    SecretKey    string   `env:"SECRET_KEY,required"`
}

From file

The env tag option file (e.g., env:"tagKey,file") can be added to 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 Example below

package main

import (
	"fmt"
	"time"
	"github.com/caarlos0/env"
)

type config struct {
	Secret       string   `env:"SECRET,file"`
	Password     string   `env:"PASSWORD,file" envDefault:"/tmp/password"`
	Certificate  string   `env:"CERTIFICATE,file" envDefault:"${CERTIFICATE_FILE}" envExpand:"true"`
}

func main() {
	cfg := config{}
	if err := env.Parse(&cfg); err != nil {
		fmt.Printf("%+v\n", err)
	}

	fmt.Printf("%+v\n", cfg)
}
$ echo qwerty > /tmp/secret
$ echo dvorak > /tmp/password
$ echo coleman > /tmp/certificate

$ SECRET=/tmp/secret  \
	CERTIFICATE_FILE=/tmp/certificate \
	go run main.go
{Secret:qwerty Password:dvorak Certificate:coleman}

Options

Environment

By setting the Options.Environment map you can tell Parse to add those keys and values as env vars before parsing is done. These envs are stored in the map and never actually set by os.Setenv. This option effectively makes env ignore the OS environment variables: only the ones provided in the option are used.

This can make your testing scenarios a bit more clean and easy to handle.

package main

import (
	"fmt"
	"log"

	"github.com/caarlos0/env"
)

type Config struct {
	Password string `env:"PASSWORD"`
}

func main() {
	cfg := &Config{}
	opts := &env.Options{Environment: map[string]string{
		"PASSWORD": "MY_PASSWORD",
	}}

	// Load env vars.
	if err := env.Parse(cfg, opts); err != nil {
		log.Fatal(err)
	}

	// Print the loaded data.
	fmt.Printf("%+v\n", cfg.envData)
}

Changing default tag name

You can change what tag name to use for setting the env vars by setting the Options.TagName variable.

For example

package main

import (
	"fmt"
	"log"

	"github.com/caarlos0/env"
)

type Config struct {
	Password string `json:"PASSWORD"`
}

func main() {
	cfg := &Config{}
	opts := &env.Options{TagName: "json"}

	// Load env vars.
	if err := env.Parse(cfg, opts); err != nil {
		log.Fatal(err)
	}

	// Print the loaded data.
	fmt.Printf("%+v\n", cfg.envData)
}

Stargazers over time

Stargazers over time

Owner
Carlos Alexandro Becker
Creator @goreleaser; SRE @totvslabs; I try to delete more code than I write.
Carlos Alexandro Becker
Comments
  • Introduce support for custom parsers + url.URL

    Introduce support for custom parsers + url.URL

    Previous PR was based on an outdated fork. Woops! This should be better.

    • Added support for url.URL
    • Added support for custom parsers via ParseWithFuncs() (new func to avoid breaking backwards compat)
    • Updated documentation
    • Updated tests (100% cov for all new functionality)

    @caarlos0 can you take a peek at this?

  • Expand support for custom parsers to non-struct types

    Expand support for custom parsers to non-struct types

    Sometimes you end up with having types in your config that are essentially int or string. An example of this would be log levels. I'm using logrus, and would like to be able to set LOG_LEVEL=debug, and map it directly on to my config struct:

    
    type Config struct {
        LogLevel logrus.Level `env:"LOG_LEVEL" envDefault:"debug"`
    }
    
    func logLevelParser(v string) (interface{}, error) {
        return logrus.ParseLevel(v)
    }
    
    func Get() (*Config, error) {
        c := &Config{}
        parsers := env.CustomParsers{
            reflect.TypeOf(logrus.GetLevel()): logLevelParser,
        }
        if err := env.ParseWithFuncs(c, parsers); err != nil {
            return nil, err
        }
        return c, nil
    }
    

    I like the custom parser support, but I think it would be even better still if we could add parser functions for basic types (ie non-struct types). Hence the PR.

    It's a small change, but I think quite a useful addition to the package. I've added 1 test covering this case. Let me know if more tests are required.

  • panic: reflect: reflect.Value.Set using value obtained using unexported field

    panic: reflect: reflect.Value.Set using value obtained using unexported field

    It's mandatory to use exported fields on the struct, which we need to parse. We should able to use unexported fields as well.

    Steps to reproduce

    • Use https://github.com/caarlos0/env#example
    • Rename Home field of config struct to home (Line 11)
    • run PRODUCTION=true HOSTS="host1:host2:host3" DURATION=1s go run main.go

    Expected

    • output {Home:/your/home Port:3000 IsProduction:true Hosts:[host1 host2 host3] Duration:1s}

    Actual

    panic: reflect: reflect.Value.Set using value obtained using unexported field
    
    goroutine 1 [running]:
    reflect.flag.mustBeAssignable(0x1b8)
            /usr/local/go/src/reflect/value.go:231 +0x1bd
    
  • Adding a general-purpose, optional zero-overhead per-field convenience callback:

    Adding a general-purpose, optional zero-overhead per-field convenience callback:

    --- working in a project right now where we want to log the env-vars on-service-startup, and also present them in a "service self-description API endpoint".

    Since this is already relying on your neat env package, it would be ugly to recreate your existing reflect traversals for our purposes here. With this new optional general-purpose callback OnEnvVarSet(field, value) anyone can easily achieve such (or similar, or other) custom-requirements with env =)

    The first commit in here is not pertinent, simply some minor hygiene. The second commit is the meat of this PR, as per above.

  • maintainers?!

    maintainers?!

    is anyone interested in helping me maintain this repo?

    I have lots of OSS repos and this one needs more love from my part, but at the same time I don't have much free time lately... maybe someone is interested in helping?

  • Pointer type support

    Pointer type support

    I have a global var Dev, but i want to use it in submodule only when submodule's Config.Dev is not set, so i must use *bool, can you support this? Thanks!

    package main
    
    import (
    	"github.com/caarlos0/env"
    	"github.com/golang/glog"
    )
    
    type Config struct {
    	Dev *bool `env:"DEV"`
    }
    
    func main() {
    	config := new(Config)
    	err := env.Parse(config)
    	glog.Errorln(config.Dev, err)
    }
    

    output:

    <nil> Type is not supported
    
  • added conditional values

    added conditional values

    Main inspiration was to get something like NODENV and similar tools that use it. Looking around I didn't find anything too similar. Basic idea is that providing cases a list of options, and using the resolved env var as key to the specified options (in the example's case, prod and dev).

  • Breaking change with uninitialised pointer fields

    Breaking change with uninitialised pointer fields

    I was using this package to set some fields of a struct, while other fields of the same struct were not controlled using environment variables.

    This worked fine, until recently - unfortunately not sure exactly when the change was introduced, but now the following example main.go

    package main
    
    import (
    	"github.com/caarlos0/env"
    	"log"
    	"net/http"
    )
    
    type Session struct {
    	Token      string `env:"TOKEN"`
    	HttpClient *http.Client
    }
    
    func main() {
    	sess := Session{}
    	err := env.Parse(&sess)
    	if err != nil {
    		log.Fatal(err)
    	}
    	sess.HttpClient = &http.Client{}
    }
    

    produces the output

    $ go run main.go
    2017/11/21 10:57:04 Expected a pointer to a Struct
    exit status 1
    

    but used to work just fine.

    Now maybe it was coincidence that it worked just fine - maybe the package doesn't support structs that have fields that are not supposed to be handled by env vars.

    But if it is supposed to be a feature (and I think at least I would like it to be ^^), then the above is probably a bug.

    A workaround, btw, is to move the assignment before the parsing, as in

    	sess := Session{}
    	sess.HttpClient = &http.Client{}
    	err := env.Parse(&sess)
    

    This won't throw an error.

  • Support UnmarshalText interface

    Support UnmarshalText interface

    Hi, I saw this library on the gophers slack and the one thing I think it's missing for me is the ability to support types that implement the TextUnmarshaler interface out of the box, like https://github.com/kelseyhightower/envconfig does.

  • Broken go.mod

    Broken go.mod

    It looks like this commit broke the go.mod file:

    https://github.com/caarlos0/env/commit/0b4f1fc4774952aad2a0e3d796bf446cc294960d

    Latest working version that I can update to and go mod tidy goes through is 5.1.1, 5.1.2 fails with following message:

    go: github.com/caarlos0/[email protected]+incompatible: go.mod has post-v5 module path "github.com/caarlos0/env/v5" at revision v5.1.2

    caarlos0/env is listed as github.com/caarlos0/env v5.1.1+incompatible in my go.mod file (upgrading from v4).

  • Fix bug when parsing env variables set and empty

    Fix bug when parsing env variables set and empty

    the following bugs were found:

    • default bug fixed: when environment variable does not exists, default value will be chosen... But when environment variable exists and it is empty, that empty string will be chosen.

    • required bug fixed: when environment variable does not exists, in error will be thrown... But when environment variable exists and it is empty, no error will be throw.

  • Support error types

    Support error types

    This is an enhancement related to that discussion: #238

    The purpose of this PR is to make a possibility convert an error to its specific type.

    Since the errors aggregation has been added before (#233), user should convert an error result to AggregateError first and then iterate over accumulated errors, example:

            err := Parse(&config{})
    	if e, ok := err.(*AggregateError); ok {
    		for _, er := range e.Errors {
    			switch v := er.(type) {
    			case ParseError:
    				// handle it
    			case NotStructPtrError:
    				// handle it
    			case NoParserError:
    				// handle it
    			case NoSupportedTagOptionError:
    				// handle it
    			default:
    				fmt.Printf("Unknown type %v", v)
    			}
    		}
    	}
    
  • Proposal: Support for error types

    Proposal: Support for error types

    Hello, At the moment, it's not possible to differentiate errors via error types. This could help determine the error directly instead of using, e.g. strings.Contains.

    For example:

    if err := env.Parse(c); err != nil {
    	if err == env.ErrMissingRequired {
    	}
    	if err == env.ErrEmpty {
    	}
    }
    
  • Pointer structs not created with env prefixes

    Pointer structs not created with env prefixes

    Following the issue raised in #202, if a pointer struct is used then the default object is not created - this is different to a non-pointer object. See the following example:

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"log"
    	"os"
    
    	"github.com/caarlos0/env/v6"
    )
    
    type Test struct {
    	Str string `env:"TEST"`
    }
    type ComplexConfig struct {
    	Foo   *Test `envPrefix:"FOO_"`
    	Bar   Test  `envPrefix:"BAR_"`
    	Clean *Test
    }
    
    func main() {
    	os.Setenv("FOO_TEST", "kek")
    	os.Setenv("BAR_TEST", "lel")
    
    	cfg := ComplexConfig{}
    	err := env.Parse(&cfg)
    	if err != nil {
    		log.Fatal(err)
    	}
    	confBytes, _ := json.Marshal(cfg)
    	fmt.Printf("%s", confBytes)
    }
    

    This results in:

    {"Foo":null,"Bar":{"Str":"lel"},"Clean":null}
    

    Notice how Foo is null, but Bar has the object created.

Un-marshaling environment variables to Go structs

envcfg Un-marshaling environment variables to Go structs Getting Started Let's set a bunch of environment variables and then run your go app #!/usr/bi

Sep 26, 2022
Small library to read your configuration from environment variables

envconfig envconfig is a library which allows you to parse your configuration from environment variables and fill an arbitrary struct. See the example

Nov 3, 2022
Go helpers to manage environment variables

Envh This library is made up of two parts : Env object : it wraps your environments variables in an object and provides convenient helpers. Env tree o

Sep 26, 2022
goconfig uses a struct as input and populates the fields of this struct with parameters from command line, environment variables and configuration file.

goconfig goconfig uses a struct as input and populates the fields of this struct with parameters from command line, environment variables and configur

Dec 15, 2022
A Go port of Ruby's dotenv library (Loads environment variables from `.env`.)

GoDotEnv A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file) From the original Library: Storing configuration in the

Jan 5, 2023
🛠 A configuration library for Go that parses environment variables, JSON files, and reloads automatically on SIGHUP
🛠 A configuration library for Go that parses environment variables, JSON files, and reloads automatically on SIGHUP

config A small configuration library for Go that parses environment variables, JSON files, and reloads automatically on SIGHUP. Example func main() {

Dec 11, 2022
Golang library for managing configuration data from environment variables

envconfig import "github.com/kelseyhightower/envconfig" Documentation See godoc Usage Set some environment variables: export MYAPP_DEBUG=false export

Dec 26, 2022
A Go library for parsing struct tags from environment variables.

Envconfig Envconfig populates struct field values based on environment variables or arbitrary lookup functions. It supports pre-setting mutations, whi

Jan 2, 2023
Environment variables substitution for Go

envsubst Environment variables substitution for Go. see docs below Installation: From binaries Latest stable envsubst prebuilt binaries for 64-bit Lin

Jan 1, 2023
Quickly read variables from environment files

go-quick-env Quickly read variables from environment files The best way to import environment variables to your code, is by using .env files. This lib

May 11, 2021
Read files into environment variables and execute command

read-file-to-env -- Read files into environment variables and execute command Example use: read-file-to-env -one-line=HOST=/etc/hostname sh -c 'echo h

Nov 12, 2021
A mapper of ENVironment variables to Structure for Go

envs a mapper of ENVironment variables to a Structure for Go. This library maps the environment variables to the struct according to the fields' types

Dec 3, 2021
Golang library for reading properties from configuration files in JSON and YAML format or from environment variables.

go-config Golang library for reading properties from configuration files in JSON and YAML format or from environment variables. Usage Create config in

Aug 22, 2022
Environment variables configuration package for Go microservices.

gocfg Environment variables configuration package for Go microservices. It helps validate environment variable values and set default values if needed

Dec 30, 2021
formicidate is a small tool for Go application can update the value of environment variables in a .env file with code

formicidae Update .env files in Go with code. What is fomicidae? formicidate is a small tool for Go application. You can update the value of environme

Jan 23, 2022
Lightweight package that makes easier and safer to deal with environment variables.

Envisage A lightweight package that makes easier and safer to deal with environment variables. Example Try it on On GoPlay https://goplay.tools/snippe

Apr 11, 2022
Tmpl - A tool to apply variables from cli, env, JSON/TOML/YAML files to templates

tmpl allows to apply variables from JSON/TOML/YAML files, environment variables or CLI arguments to template files using Golang text/template and functions from the Sprig project.

Nov 14, 2022
Library for setting values to structs' fields from env, flags, files or default tag

Configuration is a library for injecting values recursively into structs - a convenient way of setting up a configuration object. Available features:

Dec 7, 2022