`process.env` — Read Config From the Environment
Environment Variables
Read env vars at runtime, load .env files, keep secrets out of source.
What you'll learn
- Read process.env
- Load .env with --env-file or dotenv
- Avoid leaking secrets
Configuration that varies per environment (dev, staging, prod) lives
in environment variables, not in code. Node exposes them on
process.env.
Reading
const port = process.env.PORT ?? 3000;
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
console.error("DATABASE_URL is required");
process.exit(1);
} Set them when running:
PORT=8080 node server.mjs
# or export for the session
export DATABASE_URL=postgres://...
node server.mjs .env Files
Don’t type env vars on every run. Put them in a .env file:
# .env
PORT=8080
DATABASE_URL=postgres://localhost/myapp
API_KEY=secret-key-here Node 20.6+ loads it natively:
node --env-file=.env server.mjs For older Node, use the dotenv package:
import "dotenv/config";
console.log(process.env.PORT); // 8080 .env Conventions
.env.local— your machine, never committed.env.development,.env.production— defaults checked in.env.example— template with empty values, checked in
Always .env in .gitignore. Committing secrets to git is one
of the most common production incidents in the industry.
Types Are All Strings
process.env.PORT is a string. Coerce explicitly:
const port = Number(process.env.PORT ?? 3000);
const debug = process.env.DEBUG === "true"; For real apps, validate env at boot with Zod:
import { z } from "zod";
const env = z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(20),
}).parse(process.env);
// env.PORT is now a number, env.DATABASE_URL is guaranteed Crash early at startup beats crashing mid-request.
Modules →