Managing environment variables
Using the make run/api command to run our API application opens up an opportunity to tweak our command-line flags, and remove the default value for our database DSN from the main.go file. Like so:
package main ... func main() { var cfg config flag.IntVar(&cfg.port, "port", 4000, "API server port") flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production)") // Use the empty string "" as the default value for the db-dsn command-line flag, // rather than os.Getenv("GREENLIGHT_DB_DSN") like we were previously. flag.StringVar(&cfg.db.dsn, "db-dsn", "", "PostgreSQL DSN") ... }
Instead, we can update our makefile so that the DSN value from the GREENLIGHT_DB_DSN environment variable is passed in as part of the rule. If you’re following along, please go ahead and update the run/api rule as follows:
... ## run/api: run the cmd/api application .PHONY: run/api run/api: go run ./cmd/api -db-dsn=${GREENLIGHT_DB_DSN} ...
This is a small change but a really nice one, because it means that the default configuration values for our application no longer change depending on the operating environment. The command-line flag values passed at runtime are the sole mechanism for configuring our application settings, and there are still no secrets hard-coded in our project files.
During development running our application remains nice and easy — all we need to do is type make run/api, like so:
$ make run/api go run ./cmd/api -db-dsn=postgres://greenlight:pa55word@localhost/greenlight time=2023-09-10T10:59:13.722+02:00 level=INFO msg="database connection pool established" time=2023-09-10T10:59:13.722+02:00 level=INFO msg="starting server" addr=:4000 env=development
Using a .envrc file
If you like, you could also remove the GREENLIGHT_DB_DSN environment variable from your $HOME/.profile or $HOME/.bashrc files, and store it in a .envrc file in the root of your project directory instead.
If you’re following along, go ahead and create a new .envrc file like so:
$ touch .envrc
export GREENLIGHT_DB_DSN=postgres://greenlight:pa55word@localhost/greenlight
You can then use a tool like direnv to automatically load the variables from the .envrc file into your current shell, or alternatively, you can add an include command at the top of your Makefile to load them instead. Like so:
# Include variables from the .envrc file include .envrc ## help: print this help message .PHONY: help help: @echo 'Usage:' @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' ...
This approach is particularly convenient in projects where you need to make frequent changes to your environment variables, because it means that you can just edit the .envrc file without needing to reboot your computer or run source after each change.
Another nice benefit of this approach is that it provides a degree of separation between variables if you’re working on multiple projects on the same machine.
In a few chapters’ time we’ll start version controlling our codebase with Git, so let’s preemptively add an ignore rule so that the .envrc file is never committed.
$ echo '.envrc' >> .gitignore