
Managing Local Environment Variables - direnv
Problem
During development, loading and reading local environment variables can cause many issues. If I do not use Docker and do not load variables through it, adding them in the standard Linux way using 'export NAME=VALUE' in the terminal is very cumbersome.
Usually, packages that allow automatic loading of variables from a file, such as .env, come to the rescue. For Node, this is the well-known dotenv. Using it is very simple: we create a .env file and add all the environment variables we want to use during development.
PORT=5000Using an example application from a previous article, the dotenv package can be used as follows:
import express, { Request, Response } from "express";
import * as dotenv from 'dotenv' // import package
dotenv.config() // load variables into process.env
const app = express();
const PORT = process.env.PORT;
app.get("/", (req: Request, res: Response) => {
res.send("Hello World!");
});
app.listen(PORT, () => {
console.log('Server started on port ${PORT}!');
});
Now, simply install the package as a dev dependency.
npm i -D dotenv
Thanks to this package, we have access to all environment variables. Moreover, if 'dotenv.config()' is called in the main application file (app entry point), all imported modules will also have access to the variables via the 'process.env' object.
This solution is not inherently bad. Everything works correctly, but the issue arises with the additional dependency that is used only for development. Also, the two extra lines of code are not a big deal, but why add them if they are not necessary?
direnv
direnv is not a Node dependency but a shell extension. It allows automatic loading of environment variables based on the directory you are in. It only requires the presence of a '.envrc' file in the directory specifying how the variables should be loaded, and it loads them into a sub-shell of the terminal accordingly.
Installation
Most Linux distributions and macOS already have this program installed.
For it to work correctly with the terminal shell, you need to attach its execution in the shell configuration file:
- For BASH, modify ~/.bashrc: 'eval "$(direnv hook bash)"'
- For ZSH, modify ~/.zshrc: 'eval "$(direnv hook zsh)"'
For more supported shells, refer to the documentation.
Simple Usage
Now, to use environment variables just like before, i.e., loading them from a .env file, simply add another file: .envrc. Then, run the command 'direnv allow .', which allows the direnv package to automatically load environment variables.
To continue loading variables from the .env file, we can instruct the package to load variables from that file using the command 'direnv dotenv [shell] [path_to_.env_file]', in our case:
direnv dotenv zsh ./.env
Now, when we start the application in the same directory, it will have all the variables specified in the file loaded.
To simplify the entire workflow, we can specify the 'dotenv' command directly in the .envrc file.
dotenvIn this case, as long as .env and .envrc are in the same directory, the direnv package will always automatically load the variables in that directory. This allows us to remove the dotenv dependency and all related code fragments. This solution also works independently of the programming language of the application!