Environment Variables and Configuration
# Environment Variables and Configuration
Welcome to Chapter 27! We are nearing production readiness. Throughout this course, we have hardcoded sensitive information directly into our app.js file:
-
const dbURI = 'mongodb+srv://admin:mySuperSecretPassword@...'
-
const JWT_SECRET = 'mySuperSecretKey123'
If you upload this code to GitHub, anyone in the world can read your password, log into your MongoDB database, and delete or steal all your users' data. This happens to beginner developers every single day.
In this chapter, we will learn how to extract secrets from our codebase using Environment Variables.
---
1. Introduction
Environment Variables are variables that are defined outside of your application, directly on the operating system or server that is running the code.
By storing passwords in the "Environment" rather than in the code, we achieve two things:
- 1. Security: Our codebase (which goes to GitHub) contains zero passwords.
- 2. Flexibility: When running the app on our laptop, the database variable points to our local database. When we deploy to the cloud, the exact same code runs, but the cloud environment variable points to the production database.
In Node.js, we manage these locally using an NPM package called dotenv.
---
2. Learning Objectives
By the end of this chapter, you will be able to:
-
Understand the global
process.envobject.
-
Install and configure the
dotenvpackage.
-
Create a
.envfile to store sensitive keys.
-
Prevent the
.envfile from being uploaded to GitHub using.gitignore.
- Access environment variables inside your Express application.
---
3. Beginner-Friendly Explanations
The .env File
A .env (pronounced "dot-env") file is a simple text file you create in the root folder of your project. You write your passwords in it.
The dotenv Package
Node.js cannot read the .env file by default. The dotenv package acts as a bridge. When your app starts, dotenv reads the .env file, grabs all the passwords, and loads them into Node's global process.env object.
The .gitignore File
Git tracks all files in your folder. To prevent Git from tracking your passwords, you create a .gitignore file and type .env inside it. Now, when you upload to GitHub, the .env file is left behind on your laptop!
---
4. Syntax Explanation
Let's look at how to structure these files.
The .env file (NO quotes around strings!):
```text id="ch27-syntax-1"
PORT=3000
MONGO_URI=mongodb+srv://admin:securePass123@cluster.mongodb.net/DB
JWT_SECRET=superSecretKey
javascript id="ch27-syntax-2" // 1. Require and configure dotenv at the VERY TOP of your file require('dotenv').config();
const express = require('express'); const app = express();
// 2. Access the variables using process.env const port = process.env.PORT || 5000; const dbString = process.env.MONGO_URI;
console.log(Connecting to database at: ${dbString});
app.listen(port, () => console.log(Server running on port ${port}));
text id="ch27-code-1" # Dependency directories node_modules/
# Secrets .env
javascript id="ch27-code-2" // If process.env.PORT is missing, use 3000 const PORT = process.env.PORT || 3000;
// If the JWT secret is missing, crash the app immediately! // (Better to crash than to run insecurely) if (!process.env.JWT_SECRET) { console.error("FATAL ERROR: JWT_SECRET is not defined."); process.exit(1); }
javascript id="ch27-code-3" require('dotenv').config();
module.exports = { port: process.env.PORT || 3000, dbUri: process.env.MONGO_URI, jwtSecret: process.env.JWT_SECRET, environment: process.env.NODE_ENV || 'development' };
javascript id="ch27-code-4" const config = require('./config'); const express = require('express'); const app = express();
app.listen(config.port, () => console.log(Running on ${config.port}));
text PORT=4000 NODE_ENV=development API_KEY=dev_key_999
javascript id="ch27-mini-project" // MUST BE AT THE TOP require('dotenv').config();
const express = require('express'); const app = express();
const PORT = process.env.PORT || 3000; const ENV = process.env.NODE_ENV || 'development'; const KEY = process.env.API_KEY;
// Route changes based on environment app.get('/status', (req, res) => { if (ENV === 'development') { res.json({ status: "Running in DEV mode", key_exposed_for_debugging: KEY }); } else if (ENV === 'production') { res.json({ status: "Running in PROD mode", key: "HIDDEN FOR SECURITY" }); } });
app.listen(PORT, () => {
console.log(Server initialized on port ${PORT});
console.log(Current Environment: ${ENV.toUpperCase()});
});
``
Run it:
node app.js. Visit /status.
Then, go into .env, change NODE_ENV=production, restart the server, and visit /status again!
---
12. Coding Challenges
Challenge 1: Create a global error handler middleware. Use an if statement to check process.env.NODE_ENV. If it is 'development', send back err.stack in the JSON response. If it is 'production', do not send the stack.
Challenge 2: Remove the require('dotenv').config() from your app.js. Instead, run your app from the terminal using the preload flag: node -r dotenv/config app.js. This is a more advanced, cleaner way to inject variables without modifying your source code!
---
13. MCQs with Answers
Q1: What is the primary purpose of Environment Variables? A) To make the code run faster on different operating systems. B) To securely store sensitive data (like passwords) outside of the source code. C) To define global CSS variables for the frontend. D) To format JSON responses. Answer: B
Q2: Which NPM package is used to load variables from a .env file into Node.js?
A) env-loader
B) express-env
C) dotenv
D) config-env
Answer: C
Q3: How do you access an environment variable named API_KEY in your JavaScript code?
A) env.API_KEY
B) process.env.API_KEY
C) global.API_KEY
D) require('API_KEY')
Answer: B
Q4: Which file must you create to prevent Git from uploading your .env file to GitHub?
A) .gitignore
B) .envignore
C) package.json
D) .gitblock
Answer: A
---
14. Interview Questions
-
1.
Why should you never commit a .env
file to version control (Git)?
file contains highly sensitive secrets like database passwords, JWT secrets, and third-party API keys. If committed to a public repository, anyone can extract these keys and compromise your database or incur charges on your cloud accounts.
-
2.
If the
.env file isn't uploaded to the production server, how does the production server get the environment variables?
*Answer:* Production hosting platforms (like Render, Heroku, or AWS) provide a "Settings" or "Config Vars" dashboard in their web UI. You manually type your keys and passwords directly into their dashboard, and the platform injects them directly into the Node.js OS environment when the server boots up.
---
15. FAQs
Q: Can I use
.env variables in the frontend (like React)?
A: Backend .env variables are completely hidden. If you are using React or Vite, they have their own specific rules for environment variables (usually requiring a prefix like REACT_APP_ or VITE_), but you should *never* put a backend database password in a frontend env file!
Q: I added a variable to
.env but my app says it's undefined. Why?
A: 99% of the time, this is because you didn't restart your server. Nodemon does not watch the .env file for changes. Stop the terminal (Ctrl+C) and start it again.
---
16. Summary
-
Environment Variables separate sensitive configuration from source code.
-
Store keys locally in a
.env file.
-
Prevent uploading secrets by adding
.env to your .gitignore file.
-
Use the
dotenv package to load the file into Node.js.
-
Access variables globally using
process.env.VARIABLE_NAME.
-
Always provide fallback values using
||`.
---
17. Next Chapter Recommendation
Your app is now secure, connected to a database, and properly configured. Before we release it to the world, we need to ensure our code meets professional standards. In Chapter 28: Node.js Best Practices and Security, we will learn about folder architecture, rate limiting, and protecting our Express server against common hacking attempts.