How do you manage configuration and secrets in a Node.js app?
Take a moment.
Say your answer out loud or write it down first — retrieval is what makes it stick. Reveal when you're ready.
Short answer
Read from environment variables (process.env), load .env files locally with dotenv, validate at startup with a schema (zod/envalid), keep secrets out of code/git, and use platform secret managers in production.
Detailed explanation
Twelve-factor principles drive the pattern: config varies per deployment, code shouldn't. process.env provides values; dotenv (now built into Node 20+ via --env-file flag) hydrates them locally from .env files that stay gitignored alongside a committed .env.example documenting required keys. Startup validation catches misconfiguration before serving traffic: parse env into a typed config object once, fail fast with clear messages listing missing variables — infinitely better than undefined leaking three requests deep. Production secret storage shifts to platform features (Vercel/AWS/GCP secret stores, Kubernetes secrets, Vault) injected as env at runtime — same interface, safer custody. Layered defaults help: config.get('port', 3000) style accessors. Anti-patterns worth naming: config modules with hardcoded fallback credentials, importing env values at module scope before tests set them, and committing .env files 'just temporarily'.
Example
// config.ts — validated once at startup
import { z } from 'zod';
const Env = z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
NODE_ENV: z.enum(['development', 'test', 'production']),
});
export const config = Env.parse(process.env); // throws early w/ clear errors
// Run locally: node --env-file=.env index.jsInterview tip
Emphasize fail-fast validation — most candidates mention dotenv and stop. Naming --env-file shows current Node knowledge; mentioning rotation of leaked keys shows ops awareness.
Common mistakes
- Committing .env files with real secrets.
- Scattering process.env.X across the codebase without validation.