Introduction to the

The twelve-Factor application advocates storing the configuration in environment variables. Anything that needs to be changed when you switch from development to production is extracted from the code into environment variables. However, in real development, setting environment variables is not practical if multiple projects are running on the same machine. The godotenv library reads the configuration from the. Env file and stores it in the program’s environment variables. It’s very convenient to use read in your code. Godotenv comes from dotenv, an open source Ruby project.

Quick to use

Third-party libraries need to be installed first:

$ go get github.com/joho/godotenv
Copy the code

After using:

package main

import (
  "fmt"
  "log"
  "os"

  "github.com/joho/godotenv"
)

func main(a) {
  err := godotenv.Load()
  iferr ! =nil {
    log.Fatal(err)
  }

  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("age: ", os.Getenv("age"))}Copy the code

Then add a. Env file in the same directory as the executable:

name = dj
age = 18
Copy the code

Run the program, output:

name:  dj
age:  18
Copy the code

Visible, very convenient to use. By default, godotenv reads the. Env file in the root directory of the project, which uses the key = value format, one key/value pair per line. Godotenv.load () can be loaded, and os.getenv (“key”) can be read directly. Os.getenv is used to read environment variables:

package main

import (
  "fmt"
  "os"
)

func main(a) {
  fmt.Println(os.Getenv("GOPATH"))}Copy the code

Advanced features

Automatic loading

If you’re lazy in the good tradition of programmers, you probably don’t even want to call the Load method yourself. Never mind, Godotenv gives you lazy power!

Import github.com/joho/godotenv/autoload, configuration will be read automatically:

package main

import (
  "fmt"
  "os"

  _ "github.com/joho/godotenv/autoload"
)

func main(a) {
  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("age: ", os.Getenv("age"))}Copy the code

Note that since the godotenv library is not explicitly used in the code, an empty import is used, that is, an _ is added before the package name.

The autoload library calls the Load method for you:

// src/github.com/joho/godotenv/autoload/autoload.go
package autoload

/* You can just read the .env file on import just by doing import _ "github.com/joho/godotenv/autoload" And bob's your mother's brother */

import "github.com/joho/godotenv"

func init(a) {
  godotenv.Load()
}
Copy the code

Look carefully at the notes, programmer’s bad taste 😂!

Load a custom file

By default, the. Env file in the project root directory is loaded. Of course we can load files with any name, and files don’t have to end with. Env:

package main

import (
  "fmt"
  "log"
  "os"

  "github.com/joho/godotenv"
)

func main(a) {
  err := godotenv.Load("common"."dev.env")
  iferr ! =nil {
    log.Fatal(err)
  }

  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("version: ", os.Getenv("version"))
  fmt.Println("database: ", os.Getenv("database"))}Copy the code

Contents of the common file:

Name = awesome Web version = 0.0.1Copy the code

Dev env:

database = sqlite
Copy the code

Production. Env:

database = mysql
Copy the code

Run it yourself and see the results!

Note: Load takes multiple file names as arguments, and if no file name is passed, the contents of the. Env file are read by default. If the same key exists in multiple files, the first one takes precedence and the second one does not take effect. So, what is the database output above?

annotation

Comments can be added to the.env file, starting with # and ending on the line.

# app name
name = awesome web
# current versionVersion = 0.0.1Copy the code

YAML

Env files can also be in YAML format:

Name: awesome Web Version: 0.0.1Copy the code
package main

import (
  "fmt"
  "os"

  _ "github.com/joho/godotenv/autoload"
)

func main(a) {
  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("version: ", os.Getenv("version"))}Copy the code

Environment variables are not stored

Godotenv allows you to not store the contents of a. Env file into an environment variable. Using godotenv.read () returns a map[string]string, which can be used directly:

package main

import (
  "fmt"
  "log"

  "github.com/joho/godotenv"
)

func main(a) {
  var myEnv map[string]string
  myEnv, err := godotenv.Read()
  iferr ! =nil {
    log.Fatal(err)
  }

  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])}Copy the code

Direct operation map, simple and direct!

The data source

In addition to reading files, you can also read configurations from string from IO.Reader:

package main

import (
  "fmt"
  "log"

  "github.com/joho/godotenv"
)

func main(a) {
  content := 'name: awesome Web version: 0.0.1'
  myEnv, err := godotenv.Unmarshal(content)
  iferr ! =nil {
    log.Fatal(err)
  }

  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])}Copy the code

As long as the IO.Reader interface is implemented, it can be used as a data source. Can be read from File (os.file), network (net.conn), bytes.buffer, and many other sources:

package main

import (
	"bytes"
	"fmt"
	"log"
	"os"

	"github.com/joho/godotenv"
)

func main(a) {
  file, _ := os.OpenFile(".env", os.O_RDONLY, 0666)
  myEnv, err := godotenv.Parse(file)
  iferr ! =nil {
    log.Fatal(err)
  }

  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])

  buf := &bytes.Buffer{}
  buf.WriteString("name: awesome web @buffer")
  buf.Write([]byte{'\n'})
  buf.WriteString("version: 0.0.1")
  myEnv, err = godotenv.Parse(buf)
  iferr ! =nil {
    log.Fatal(err)
  }

  fmt.Println("name: ", myEnv["name"])
  fmt.Println("version: ", myEnv["version"])}Copy the code

Note that the methods used to read from a string are different from those used to read from IO.Reader. The former is Unmarshal and the latter is Parse.

generate.envfile

Env files can be generated programmatically, and can be written directly to the file:

package main

import (
  "bytes"
  "log"

  "github.com/joho/godotenv"
)

func main(a) {
  buf := &bytes.Buffer{}
  buf.WriteString("name = awesome web")
  buf.WriteByte('\n')
  buf.WriteString("version = 0.0.1")

  env, err := godotenv.Parse(buf)
  iferr ! =nil {
    log.Fatal(err)
  }

  err = godotenv.Write(env, "./.env")
  iferr ! =nil {
    log.Fatal(err)
  }
}
Copy the code

Check the generated. Env file:

name="awesome web"
version="0.0.1"
Copy the code

You can also return a string, which you can knead however you like:

package main

import (
  "bytes"
  "fmt"
  "log"

  "github.com/joho/godotenv"
)

func main(a) {
  buf := &bytes.Buffer{}
  buf.WriteString("name = awesome web")
  buf.WriteByte('\n')
  buf.WriteString("version = 0.0.1")

  env, err := godotenv.Parse(buf)
  iferr ! =nil {
    log.Fatal(err)
  }

  content, err := godotenv.Marshal(env)
  iferr ! =nil {
    log.Fatal(err)
  }
  fmt.Println(content)
}
Copy the code

Command line mode

Godotenv also provides a command-line mode:

$ godotenv -f ./.env command args
Copy the code

$GOPATH/bin = $GOPATH/bin = $GOPATH/bin = godotenv;

The command line mode is to read the specified file (use the. Env file if not specified by -f), set the environment variables, and then run the following program.

Let’s write a simple program to verify:

package main

import (
  "fmt"
  "os"
)

func main(a) {
  fmt.Println(os.Getenv("name"))
  fmt.Println(os.Getenv("version"))}Copy the code

Run it with Godotenv:

$ godotenv -f ./.env go run main.go
Copy the code

Output:

Awesome web 0.0.1Copy the code

Multiple environments

In practice, different files are loaded based on the value of the APP_ENV environment variable:

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/joho/godotenv"
)

func main(a) {
	env := os.Getenv("GODAILYLIB_ENV")
	if env == "" {
		env = "development"
	}

	err := godotenv.Load(".env." + env)
	iferr ! =nil {
		log.Fatal(err)
	}

	err = godotenv.Load()
	iferr ! =nil {
		log.Fatal(err)
	}

	fmt.Println("name: ", os.Getenv("name"))
	fmt.Println("version: ", os.Getenv("version"))
	fmt.Println("database: ", os.Getenv("database"))}Copy the code

We first read the environment variable GODAILYLIB_ENV, then the corresponding.env. + env, and finally the default.env file.

As mentioned earlier, the first read takes precedence. We can have the default. The foundation of the env file configuration information and some default values, if in development/test/production environment need to be modified, then the corresponding. Env. Development/env. The test/env. The production file configuration again.

.env file contents:

Name = awesome Web version = 0.0.1 database = fileCopy the code

. The env. Development:

database = sqlite3
Copy the code

. The env. Production:

database = mysql
Copy the code

Run the program:

The default is development environment$go run main.go name: awesome Web version: 0.0.1 database: sqlite3Set to build environment$GODAILYLIB_ENV=production go run main.go name: awesome Web version: 0.0.1 database: mysqlCopy the code

Point source

Godotenv reads the contents of the file, why can be accessed using os.getenv:

// src/github.com/joho/godotenv/godotenv.go
func loadFile(filename string, overload bool) error {
	envMap, err := readFile(filename)
	iferr ! =nil {
		return err
	}

	currentEnv := map[string]bool{}
	rawEnv := os.Environ()
	for _, rawEnvLine := range rawEnv {
		key := strings.Split(rawEnvLine, "=") [0]
		currentEnv[key] = true
	}

	for key, value := range envMap {
		if! currentEnv[key] || overload { os.Setenv(key, value) } }return nil
}
Copy the code

Because Godotenv calls os.setenv to set key-value pairs into environment variables.

conclusion

This article introduced the basic and advanced uses of the Godotenv library. Godotenv source code is also easier to read, there is time, interested in children’s shoes suggest a look ~

reference

  1. Godotenv GitHub Repository: github.com/joho/godote…

I

My blog

Welcome to follow my wechat public account [GoUpUp], learn together, progress together ~

This article is published by OpenWrite!