Skip to main content
Node.js Basics
CHAPTER 27 Beginner

Environment Variables and Configuration

Updated: May 13, 2026
20 min read

# 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. 1. Security: Our codebase (which goes to GitHub) contains zero passwords.
  1. 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.env object.
  • Install and configure the dotenv package.
  • Create a .env file to store sensitive keys.
  • Prevent the .env file 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

1
**The `app.js` file:**

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}));

12345678910111213141516171819
**Output Explanation:**
When `require('dotenv').config()` runs, it grabs `MONGO_URI` from the `.env` file and attaches it to `process.env`. If you delete the `.env` file, `process.env.MONGO_URI` becomes `undefined`.

---

## 5. Real-world Examples

**Payment Gateways:**
If you integrate Stripe to process credit cards, Stripe gives you a "Secret API Key." If you hardcode this key and upload it to a public GitHub repo, a bot will scan your repo, find the key, and use it to process fraudulent refunds within minutes. 
Every professional company stores Stripe keys, AWS keys, and Database keys strictly in Environment Variables.

---

## 6. Multiple Code Examples

### Example 1: The `.gitignore` file
You MUST create this file before you ever run `git init` or `git push`.

**`.gitignore` (in root directory):**

text id="ch27-code-1" # Dependency directories node_modules/

# Secrets .env

12345
Now, `node_modules` and `.env` will never be uploaded to the internet.

### Example 2: Fallback Values
What if you deploy to a server, but forget to set the `PORT` variable on the server? The app will crash. Always use the `||` (OR) operator to provide a fallback value.

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); }

1234
### Example 3: Creating a `config.js` file
As your app grows, writing `process.env.XYZ` 50 times gets messy. A best practice is to centralize them in a config file.

**`config.js`**:

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' };

1
**`app.js`**:

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}));

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
---

## 7. Output Explanations

What is `NODE_ENV`?
It is a standard environment variable used across the Node ecosystem. If you set `NODE_ENV=production`, Express automatically optimizes itself, caches templates, and hides detailed error messages to make your app run significantly faster and more securely.

---

## 8. Common Mistakes

1. **Putting spaces or quotes in `.env`:** 
   *Wrong:* `PORT = "3000"` 
   *Right:* `PORT=3000` (No spaces, no quotes).
2. **Requiring dotenv too late:** If you try to use `process.env.MONGO_URI` on line 5, but you put `require('dotenv').config()` on line 10, it will return `undefined`. The require statement must be the absolute first line of your app.
3. **Uploading `.env` to GitHub:** If you forget to add it to `.gitignore` and push it, deleting the file later doesn't help—it is permanently in the Git history! You must change all your database passwords immediately.

---

## 9. Best Practices

- **Create a `.env.example` file:** Since you don't upload the `.env` file, other developers cloning your repo won't know what variables they need to create. Make a `.env.example` file that contains the keys but NOT the passwords:
  ```text
  PORT=3000
  MONGO_URI=your_mongo_string_here
  JWT_SECRET=your_jwt_secret_here
  ```
  Upload *this* file to GitHub!
- **Restart the server:** Nodemon watches `.js` files. It does NOT watch `.env` files. If you change a variable in `.env`, you must manually restart Nodemon for the changes to take effect.

---

## 10. Exercises

1. Initialize a project. Run `npm install dotenv`.
2. Create a `.env` file. Add `MY_NAME=John` and `MY_AGE=25`.
3. Create `app.js`. Require dotenv at the top.
4. `console.log(process.env.MY_NAME)` and run the app.

---

## 11. Mini Project: Configurable app

**Objective:** Build a server that changes its behavior based on whether the `NODE_ENV` variable is set to "development" or "production".

**Step 1:** Installation (`npm install express dotenv`)

**Step 2:** Create `.env`

text PORT=4000 NODE_ENV=development API_KEY=dev_key_999

1
**Step 3:** The Server (`app.js`)

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. 1. Why should you never commit a .env file to version control (Git)?
*Answer:* The .env 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.
  1. 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.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·