Environment & Config
Configuration is the set of values that change between machines — ports, database URLs, API keys — which you keep out of your code and read from environment variables on process.env .
Learn Environment & Config in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll read environment variables with sensible defaults, load a .env file natively, convert string values to the right types, and centralize everything in a validated config module.
What You'll Learn in This Lesson
1️⃣ Reading process.env
Node puts every environment variable on the process.env object. Read a value, supply a default with ?? for when it's missing, and remember that every value is a string — convert with Number() when you need a number.
2️⃣ Loading a .env File
Typing variables on every launch gets old. A .env file holds KEY=value lines, and recent Node loads it natively with --env-file . Keep that file out of git so secrets stay private.
3️⃣ A Central, Validated Config Module
Reading process.env scattered across files gets messy. The clean pattern: a single config.js that reads, validates, and exports one object. Required values throw at startup if missing, so the app fails fast instead of breaking mysteriously later.
Your turn. Fill in the two ___ blanks to read a host and port with defaults, then run it.
Mini-Challenge: A Validated Config Module
Build a real config.js with a required() helper, a number, a required string, and a boolean parsed from a string. Import it in app.js and print all three values.
Common Errors (and the fix)
- Math gives the wrong answer — You treated an env string as a number. process.env.PORT + 1 is "30001" , not 3001 . Wrap it in Number() first.
- A "false" flag is truthy — The string "false" is truthy in JS. Compare explicitly: process.env.DEBUG === "true" .
- .env not loaded — You forgot --env-file=.env (or, on old Node, require("dotenv").config() at the top). Then process.env stays empty.
- Secret committed to git — You forgot to add .env to .gitignore . Add it, rotate the secret, and commit a .env.example instead.
- App crashes deep inside instead of at startup — Validate required vars at boot and throw a clear message, so missing config fails immediately.
Pro Tips
- 💡 One source of truth: read process.env only inside config.js ; everything else imports the config object.
- 💡 Commit a template: a .env.example with empty values documents what's needed without leaking secrets.
- 💡 Use NODE_ENV: the convention development / production lets you switch behavior, like logging detail.
📋 Quick Reference — Config in Node
Goal
Code
Read a variable
process.env.PORT
Default if missing
process.env.PORT ?? 3000
As a number
Number(process.env.PORT)
As a boolean
process.env.DEBUG === "true"
Set inline
PORT=8080 node app.js
Load a file
node --env-file=.env app.js
Frequently Asked Questions
🎉 Lesson Complete!
- ✅ Config lives in environment variables , never hard-coded
- ✅ Read them from process.env , with ?? defaults
- ✅ Every env value is a string — convert numbers and booleans
- ✅ Load a .env file with --env-file
- ✅ Keep .env in .gitignore ; validate required vars at startup
- ✅ Next lesson: Testing with node:test — prove your code works
Practice quiz
Where does Node expose environment variables?
- process.argv
- global.config
- process.env
- os.env
Answer: process.env. process.env is an object of environment variables provided by the OS or launch command.
What type is every value in process.env?
- A string
- A number
- A boolean
- It varies by key
Answer: A string. Environment variables come from the OS as text, so every process.env value is a string.
What does process.env.PORT ?? 3000 do?
- Always uses 3000
- Multiplies PORT by 3000
- Throws if PORT is set
- Uses 3000 only when PORT is undefined
Answer: Uses 3000 only when PORT is undefined. The ?? operator supplies the default 3000 only when process.env.PORT is null or undefined.
How do you turn process.env.WORKERS into a real number?
- parseEnv(process.env.WORKERS)
- Number(process.env.WORKERS)
- process.env.WORKERS.toNumber()
- It is already a number
Answer: Number(process.env.WORKERS). Use Number(...) to convert the string env value into a numeric type.
How can modern Node (v20.6+) load a .env file natively?
- node --env-file=.env app.js
- node --load-env app.js
- require('dotenv') is required
- node --config .env
Answer: node --env-file=.env app.js. Node v20.6+ reads a .env file with the --env-file flag, populating process.env before your code runs.
How do you set a variable inline for one launch?
- node app.js PORT=8080
- node --PORT 8080 app.js
- PORT=8080 node app.js
- export only in package.json
Answer: PORT=8080 node app.js. Prefixing the command, like PORT=8080 node app.js, sets the variable for that run.
What is the recommended way to handle config across your app?
- Read process.env everywhere it is needed
- Read env vars once in a config module and export a clean object
- Hard-code values in each file
- Store config in a global variable
Answer: Read env vars once in a config module and export a clean object. Best practice: read and validate env vars in one config module; the rest of the app imports config, not process.env.
How should a required env var behave when it is missing?
- Silently default to an empty string
- Retry until it appears
- Log a warning and continue
- Fail fast and loud by throwing an error at startup
Answer: Fail fast and loud by throwing an error at startup. Validate required values at startup and throw, so the app fails loudly instead of misbehaving later.
How do you convert a string env var to a boolean?
- Boolean(process.env.DEBUG)
- Compare it: process.env.DEBUG === 'true'
- process.env.DEBUG.toBool()
- It is already a boolean
Answer: Compare it: process.env.DEBUG === 'true'. Because env values are strings, the string 'false' is truthy; compare explicitly with === 'true'.
How do you keep secrets like API keys out of version control?
- Commit them in the README
- Encrypt the whole repo
- Add .env to .gitignore and commit a .env.example instead
- Put them in package.json
Answer: Add .env to .gitignore and commit a .env.example instead. Add .env to .gitignore so secrets are never pushed; commit a .env.example listing variable names with placeholders.
Continue this course
- Previous: Connecting a Database
- Next: Testing (node:test)