Express.js Mastery / Express: Let’s Start Building The Natours API!

Environment Variables

πŸ“– Context

This isn't strictly an Express concept β€” it's a general Node.js concept β€” but it's essential for moving forward in the project. Node.js/Express apps run in different environments, most commonly:

  • πŸ› οΈ Development β€” while building the app.

  • πŸš€ Production β€” once deployed live.

Depending on the environment, an app might use different databases, turn logging on/off, turn debugging on/off, and so on. These environment-specific settings are controlled through environment variables.


πŸ”Ή Step 1: Express's Default Environment Variable

Express automatically sets an environment variable by default. Open server.js file. This can be checked with:

console.log(app.get('env'));

Running this logs:

development

because Express defaults to development mode unless told otherwise.

πŸ’‘ Important Placement Note

  • πŸ“„ Since this isn't really an Express-specific concern, it should be checked/used in server.js, not app.js.

  • πŸ—οΈ This reinforces the earlier convention that app.js is reserved purely for Express configuration.


πŸ”Ή Step 2: Node.js's Own Environment Variables

Separately from Express, Node.js itself sets a large number of environment variables, accessible via:

console.log(process.env);

This returns many variables Node uses internally β€” e.g. the current working directory, home folder, login name, the script used to start the process, and more.

πŸ’‘ How this works

  • βš™οΈ These come from Node's built-in process core module.

  • βœ… process is automatically available everywhere β€” no require() needed.


πŸ”Ή Step 3: The NODE_ENV Convention

Many npm packages used in Express development rely on a specific variable called NODE_ENV, which is meant to indicate whether the app is running in development or production.

⚠️ Express does not set this automatically β€” it must be defined manually.

πŸ–₯️ Setting It via the Terminal (Quick Method)

NODE_ENV=development npm start
  • Importantβ€”That syntax works on Linux/macOS, but not on Windows PowerShell.

  • Open PowerShell in your project folder and run:

$env:NODE_ENV="development"

Then start the server:

npm start

πŸ’‘ How this works

  • βž• Prepending variables directly before the command sets them as environment variables for that process.

  • πŸ“ Multiple variables can be set this way, e.g.:

  • For MAC use this:

X=23 NODE_ENV=development npm start
  • For windows use this:

$env:X="23"; $env:NODE_ENV="development"; npm start

Then:

process.env.X

returns:

"23"
  • ⚠️ This is useful for testing but doesn't scale well β€” imagine needing to set 10+ variables this way every time.


πŸ”Ή Step 4: Using a .env Configuration File (Scalable Method)

Create a file called:

config.env

Contents:

NODE_ENV=development
PORT=3000
USERNAME=jonas
PASSWORD=123456

πŸ’‘ Why this approach is better

  • βœ… Avoids having to manually type many variables in the terminal every time.

  • πŸ“„ .env is the standard convention for this kind of configuration file.

  • 🎨 VS Code even recognizes it and shows a special configuration icon (installing a .env syntax highlighting extension makes it easier to read).

  • πŸ”  Variable names are conventionally written in UPPERCASE.


πŸ”Ή Step 5: Loading .env Variables into the App Using dotenv

Install the package:

npm install dotenv

Then in server.js:

const dotenv = require('dotenv');
dotenv.config({ path: './config.env' });

πŸ’‘ How each part works

  • πŸ“¦ dotenv is the standard npm package for reading .env files and loading their contents into process.env.

  • πŸ“„ dotenv.config({ path: ... }) reads the specified file and attaches each variable to the Node process's environment variables.

  • ⚠️ Without this step, the .env file is just a plain text file β€” Node has no automatic awareness of its contents.

🚨 Critical Ordering Issue

Initially:

const app = require('./app');

const dotenv = require('dotenv');
dotenv.config({ path: './config.env' });

❌ This caused environment variables inside app.js to be undefined.

βœ… Correct Order

const dotenv = require('dotenv');
dotenv.config({ path: './config.env' });

const app = require('./app');

πŸ’‘ Why Order Matters

  • 🌍 Environment variables are attached to the Node process.

  • πŸ”„ The whole app runs inside one process.

  • βœ… Variables set before loading other files are available everywhere afterward.

  • ❌ If a file is loaded (require) before the variables are created, that file won't see them.


πŸ”Ή Step 6: Using Environment Variables in app.js

if (process.env.NODE_ENV === 'development') {
  app.use(morgan('dev'));
}

πŸ’‘ How this works

  • πŸ“‹ Restricts the Morgan logging middleware to development mode only.

  • 🚫 Logging typically shouldn't happen in production.

  • 🌍 Even though dotenv.config() was called in server.js, process.env.NODE_ENV is still accessible here in app.js because environment variables belong to the shared Node process, not individual files.


πŸ”Ή Step 7: Using the PORT Variable

const port = process.env.PORT || 3000;

app.listen(port, () => {
  console.log(`App running on port ${port}...`);
});

πŸ’‘ Why this works

  • πŸ›‘οΈ Falls back to 3000 if PORT isn't defined.

  • βœ… This is a very common defensive pattern when reading configuration values.

  • Add console.log(process.env.NODE_ENV); in app.js file

  • Remove this line we dont need this


πŸ”Ή Step 8: Creating Separate npm Scripts for Development and Production

"scripts": {
  "start:dev": "nodemon server.js",
  "start:prod": "NODE_ENV=production nodemon server.js"
}

For Windows Use this:

"start:prod": "$env:NODE_ENV='production'; nodemon server.js"

πŸš€ Testing Production

npm run start:prod

βœ… Results:

  • 🌍 NODE_ENV = production

  • 🚫 Morgan logging is not active

This confirms the conditional middleware setup works correctly.

  • Now remove console.log(process.env.NODE_ENV); this line from app.js file:

πŸ› οΈ Development

npm run start:dev

βœ… Results:

  • 🌍 NODE_ENV = development

  • πŸ“‹ Morgan logging is active


βœ… Key Takeaway

  • 🌍 Environment variables let your app behave differently in development and production.

  • βš™οΈ app.get('env') is Express's environment setting, while process.env.NODE_ENV is set manually.

  • 🌐 process.env provides access to environment variables in any file.

  • πŸ“„ Use a .env file with dotenv to manage environment variables.

  • ⚠️ Call dotenv.config() before accessing process.env.

  • πŸ”’ Store sensitive data (API keys, passwords, etc.) in .env files.

  • ▢️ Use separate npm scripts (e.g., start:dev, start:prod) for different environments.