env
The Practical Playbook for Mastering Environment Variables in Node.js and Beyond
Key Takeaways
- Environment variables decouple configuration from code, enabling consistent behavior across development, staging, and production environments.
- Always use a
.envfile in development and a process manager (like PM2) or cloud service (like AWS ECS) to inject variables in production. - Never commit sensitive secrets (API keys, passwords) to version control; use
.gitignoreand environment variable managers instead. - Validate environment variables at application startup to fail fast and provide clear error messages.
Understanding the Role of env in Modern Software
Environment variables are the mechanism by which an operating system or process communicates configuration data to a running application. In Node.js, these are accessed via the process.env object. The primary advantage is the Twelve-Factor App methodology: "store config in the environment." This approach allows you to ship a single build artifact to different environments (dev, test, prod) without changing the codebase.
Step-by-Step Implementation Checklist
1. Define Your Configuration Needs
Identify all variables that change between environments. Common examples include:
- Database Connection Strings: e.g.,
MONGO_URI,DB_HOST,DB_PORT. - API Keys: e.g.,
STRIPE_SECRET_KEY,OPENAI_API_KEY. - Feature Flags: e.g.,
ENABLE_NEW_CHECKOUT. - Runtime Settings: e.g.,
PORT,LOG_LEVEL.
2. Local Development Setup
Use a .env file in your project root. Add it to your .gitignore immediately.
- Create a
.env.examplefile containing all required variable names (with empty or dummy values) and commit this to your repository. This serves as documentation for new developers. - Install a package like
dotenvif you are not using a framework that handles this automatically (like Next.js or Express with express-envar). - Load variables at the top of your entry point:
require('dotenv').config();
3. Production Deployment Strategy
In production, avoid using .env files. Use the infrastructure’s native secret management.
- AWS: Use AWS Systems Manager Parameter Store or AWS Secrets Manager.
- DigitalOcean: Use Platform as a Service (PaaS) environment variable settings.
- Docker: Use
docker-composefor local staging and inject variables via theenv_filedirective or Cloud Provider Secrets at runtime. - Generic: Use your process manager. For Node.js,
pm2 start app.js --env NODE_ENV=production.
Best Practices and Security Comparison
| Practice | Good | Bad |
|---|---|---|
| Secrets Management | Use a dedicated secrets vault (AWS Secrets Manager, HashiCorp Vault) for production. | Hardcoding API keys in source code or committing .env files to Git. |
| Validation | Use joi or envar to validate types and required fields at startup. |
Checking for process.env.DB_HOST deeply inside business logic. |
| Access Control | Limit who can view/rotate secrets using IAM roles or organization-level permissions. | Sharing static .env files via email or Slack channels. |
| Rotation | Automate secret rotation or have a documented manual process for quarterly rotation. | Using the same password/API key for 5+ years. |
Advanced: Validating and Parsing Environment Variables
Simply accessing process.env is not enough. You must handle edge cases like boolean strings and type coercion.
- Type Coercion:
process.env.PORTis always a string. UseparseInt(process.env.PORT, 10)when setting server ports. - Booleans:
process.env.DEBUGmight be set to the string "true". Useprocess.env.DEBUG === 'true'for strict checking. - Library Recommendation: Use envar to define a schema and validate at import time.
Frequently Asked Questions
Can I use environment variables in the browser?
No. process.env is a Node.js global. In web browsers, you must inject build-time variables using your bundler (e.g., Webpack’s DefinePlugin or Vite’s import.meta.env). Never expose sensitive secrets to the client side; they will be visible in the browser’s network tab and source code.
What happens if an environment variable is missing in production?
If you rely on process.env.MISSING_VAR, it returns undefined. If your code does not handle this gracefully (e.g., via default values or validation), the application may crash or behave unpredictably. Always implement a validation step that throws a descriptive error and prevents the service from starting if critical variables are missing.
How do I safely share non-sensitive configuration between teams?
Use a .env.example file committed to your repository. It should list all required variable names with safe, placeholder values. For example, STRIPE_PUBLISHABLE_KEY=pk_test_placeholder. This acts as a checklist for new developers and CI/CD pipelines without exposing actual secrets.