Environment Variables

`process.env` — Read Config From the Environment

Environment Variables

Read env vars at runtime, load .env files, keep secrets out of source.

3 min read Level 1/5 #nodejs#env#dotenv
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 →