How to Use Node Environment Variables with a DotEnv File for Node.js and npm
As an experienced full-stack developer, I know how crucial it is to keep sensitive configuration data like API keys, database credentials, and other secrets secure and out of your codebase. The recommended way to do this is by using environment variables.
In this comprehensive guide, we‘ll take an in-depth look at what environment variables are, why they are useful, and how to effectively manage them in your Node.js projects using the popular dotenv
npm package. I‘ll share real-world examples, best practices, and advanced tips to help you master using environment variables like a pro.
What are Environment Variables?
Environment variables are key-value pairs that are set outside of an application and made available to it at runtime. They provide a flexible way to dynamically pass configuration options to your program without hardcoding values in your source code.
Common examples of data frequently stored in environment variables include:
- API keys and secrets for third-party services
- Database connection strings and authentication credentials
- Server hostname, port, and other network settings
- Feature flags to enable/disable certain app functionality
- Environment-specific options for dev, staging, prod, etc.
Storing this type of information in environment variables is beneficial for several reasons:
-
Security: Keeping sensitive data out of your codebase reduces the risk of accidentally exposing secrets if your code is ever made public. Environment variables are not usually committed to version control.
-
Configurability: You can easily change an app‘s configuration without modifying code. This is handy when you need to run your app in different modes locally vs in production, or to quickly enable/disable features.
-
Reusability: If you build your app to expect config from environment variables, you can more easily run it in different environments that set the appropriate values, without changing the app itself.
-
Collaboration: Storing config in environment variables instead of code makes it easier to share a codebase without exposing sensitive credentials. You can provide a template with placeholder values.
Using Dotenv for Managing Environment Variables
While you can certainly set environment variables at the system level using vanilla Node.js, it‘s often more convenient to use a dedicated library for managing them, especially in development. The most popular tool for this job is dotenv
, with over 4 million weekly downloads on npm.
To use dotenv
in your project, first install it as a dependency:
npm install dotenv
Then, as early as possible in your application code, import and configure dotenv
:
require(‘dotenv‘).config();
This will look for a file named .env
in the directory where you ran your app, and automatically load any environment variables defined there into the process.env
global object.
Defining Variables in .env Files
The syntax for defining variables in your .env
file is KEY=VALUE
, with one variable per line:
# API KEYS
TWITTER_API_KEY=123abc
GITHUB_CLIENT_ID=456xyz
# DATABASE
DB_HOST=localhost
DB_PORT=5432
# OTHER
LOG_LEVEL=debug
ENABLE_CACHE=true
Quotes are only required around values if they contain whitespace. Comments can be added by starting a line with the #
character.
It‘s important to never commit your .env
file to version control, since it likely contains app secrets. Add it to your .gitignore
to be safe:
# .gitignore
.env
Using Different .env Files per Environment
For better organization and flexibility, you can use different .env
files for each environment you run your app in:
.env.development
.env.staging
.env.production
To have dotenv
load the file for a specific environment, set the NODE_ENV
environment variable when running your app:
NODE_ENV=production node app.js
Then in your code, tell dotenv
to look for a file named .env.${NODE_ENV}
:
require(‘dotenv‘).config({ path: `.env.${process.env.NODE_ENV}` });
This gives you a clean way to use different configuration locally vs in production, for example connecting to a local test database during development but a real production database when deployed.
Accessing Environment Variables in Code
With dotenv
set up, you have access to your environment variables on the process.env
object from anywhere in your Node.js code. For example:
const twitterApiKey = process.env.TWITTER_API_KEY;
const githubClientId = process.env.GITHUB_CLIENT_ID;
const dbHost = process.env.DB_HOST;
const logLevel = process.env.LOG_LEVEL;
Remember that process.env
values are always strings, so you may need to convert them to numbers, booleans, or other types as needed:
const dbPort = parseInt(process.env.DB_PORT, 10);
const enableCache = process.env.ENABLE_CACHE === ‘true‘;
It‘s also a good idea to provide sensible default values in case an expected environment variable is not set:
const port = process.env.PORT || 3000;
const host = process.env.HOST || ‘localhost‘;
Validating Required Env Vars
For required configuration that your app absolutely needs to run, like a database connection string, you can add validation to check that the necessary environment variables are set on startup:
const requiredEnvVars = [‘DB_HOST‘, ‘DB_PORT‘, ‘DB_NAME‘];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
console.error(`Error: Missing required environment variable ${envVar}`);
process.exit(1);
}
}
This will immediately fail and alert you if any required configuration is missing when you run your app, rather than failing later with a cryptic error.
Advanced Environment Variable Tips
Beyond the basics, here are some additional best practices and techniques to consider when using environment variables in your Node.js apps:
Naming Conventions
It‘s common to use uppercase strings for environment variable names, with words separated by underscores:
- API_KEY
- DATABASE_URL
- LOG_LEVEL
Whatever naming style you adopt, aim to be consistent throughout your application.
Logging and Debugging
When logging output or error messages that include environment variables, be very careful not to expose any sensitive values! It‘s easy to accidentally log an API key or database password to a file or console if you‘re not careful.
For verbose debugging output that may contain environment variables, consider making it toggleable with an env var:
if (process.env.DEBUG === ‘true‘) {
console.log(‘Debugging info:‘, {
apiKey: process.env.API_KEY,
dbPassword: process.env.DB_PASSWORD,
});
}
Then you can easily enable debug logging only when needed by setting DEBUG=true
.
Working with Hosting Providers
Most cloud hosting providers have built-in ways to set environment variables for your deployed applications, either through an admin dashboard or via CLI tools.
For example, with Heroku you can set a variable with the config
command:
heroku config:set API_KEY=mysecretkey
And with Vercel‘s vercel
command line:
vercel secrets add twitter-api-key 1x2y3z
Be sure to read your hosting provider‘s documentation on securely managing secrets and environment variables in production.
Challenges with Environment Variables
While incredibly useful, environment variables aren‘t always a perfect solution for app configuration. A few potential downsides to be aware of:
-
Unlike config files, environment variables can‘t easily be checked into version control, making it harder to track changes over time.
-
If you have many environment variables, it can be cumbersome to manage them all in one flat
.env
file. Tools likedotenv-expand
allow referencing other variables to help with this. -
Environment variables are global to a running process, so if your app deals with multiple customers or tenants, you can‘t easily use env vars to store customer-specific config. A dedicated config management system may be a better fit for complex multi-tenant use cases.
-
Setting environment variables on remote servers can be tricky and less visible than config files. Make sure you have a secure process in place to manage production secrets.
Despite these challenges, environment variables are still a best practice for secure configuration compared to storing sensitive values in code. Being aware of the limitations can help you use them effectively.
Conclusion
Environment variables are a powerful tool for storing application configuration separate from code, which improves security, flexibility, and reusability. Using the dotenv
npm package makes it super easy to manage environment variables in your Node.js projects.
By following the tips and best practices covered in this guide, you‘ll be able to leverage environment variables to build Node.js apps that are more modular, configurable, and secure. I hope you found this in-depth overview helpful!