Environment Variable Manager
This skill ensures environment variables are properly added, documented, and configured across all project files and deployment platforms.
Files to Update When Adding Environment Variables
When adding a new environment variable, the following files must be updated in this order:
1. .env.example (Required)
Add the variable with a descriptive comment explaining its purpose and expected format.
Format:
# Description of what this variable does
# Options: value1, value2, value3 (if applicable)
# Default: default_value (if applicable)
VARIABLE_NAME=example_value
Example:
# Server port - useful for local development or platform-specific configs
# Default: 3000
PORT=3000
# Node environment - affects Express server optimization
# Options: development, production, test
# Default: (unset)
NODE_ENV=production
2. server.js (Required)
Implement the variable usage in the application code.
Pattern:
// Read from environment with fallback default
const variableName = process.env.VARIABLE_NAME || 'default_value';
// For required variables (no default)
const requiredVar = process.env.REQUIRED_VAR;
if (!requiredVar) {
console.error('ERROR: REQUIRED_VAR environment variable is not set');
process.exit(1);
}
Examples:
// Optional with default
const port = process.env.PORT || 3000;
// Optional with default
const nodeEnv = process.env.NODE_ENV || 'development';
// Required variable
const apiKey = process.env.API_KEY;
if (!apiKey) {
console.error('ERROR: API_KEY environment variable must be set');
process.exit(1);
}
3. docs/ENVIRONMENT-VARIABLES.md (Required)
Add comprehensive documentation in the "Currently Supported Variables" section.
Format:
### VARIABLE_NAME
**Purpose**: Brief description of what this variable controls
**Required**: Yes/No
**Default**: `default_value` (or "None - must be set" for required vars)
**Valid Values**:
- `value1`: Description
- `value2`: Description
**Example**:
```bash
VARIABLE_NAME=example_value
Platform Notes:
- AWS Elastic Beanstalk: [specific notes if applicable]
- Google Cloud Run: [specific notes if applicable]
- Docker: [specific notes if applicable]
### 4. `CLAUDE.md` (Required)
Update the "Environment Variables" section to include the new variable in the list.
**Format**:
```markdown
**Currently Supported Variables:**
- `PORT` (optional, defaults to 3000) - Server port. Useful for local development or platform-specific configurations
- `NODE_ENV` (optional) - Node environment: `development`, `production`, or `test`
- `NEW_VARIABLE` (required/optional, defaults to X) - Brief description
5. docker-compose.yml (If Applicable)
Add to the environment section if the variable should be set in Docker containers.
Format:
services:
usa-spending-app:
environment:
- NODE_ENV=production
- PORT=3000
- NEW_VARIABLE=value
6. Dockerfile (If Applicable)
Add ENV directive if the variable needs a build-time or runtime default.
Format:
# For build-time variables
ARG VARIABLE_NAME=default_value
# For runtime variables with defaults
ENV VARIABLE_NAME=default_value
Note: Only add to Dockerfile if a default is truly needed. Prefer runtime configuration via docker-compose.yml or deployment platform.
7. Deployment Documentation (If Platform-Specific)
Update docs/DEPLOYMENT.md if the variable requires special handling on specific platforms.
Complete Checklist for Adding Environment Variables
Use this checklist to ensure all steps are completed:
- Add to
.env.examplewith descriptive comment and example value - Implement in
server.jswith appropriate default or required check - Document in
docs/ENVIRONMENT-VARIABLES.mdwith full details - Update variable list in
CLAUDE.md"Environment Variables" section - Add to
docker-compose.ymlenvironment section (if applicable) - Add to
DockerfileENV/ARG (if applicable) - Update
docs/DEPLOYMENT.mdwith platform-specific instructions (if needed) - Test locally without
.envfile (verify defaults work) - Test locally with
.envfile (verify variable is read correctly) - Verify
.envis in.gitignore(should already be there) - Never commit actual
.envfile to repository - Update
CHANGELOG.mdunder [Unreleased] > Added section
Security Rules
Sensitive Values
API Keys, Passwords, Secrets, Tokens:
- NEVER hardcode in source code
- NEVER commit to
.env.examplewith real values (use placeholders) - MUST use platform-specific secret managers in production:
- AWS: AWS Secrets Manager or Parameter Store
- Google Cloud: Secret Manager
- Heroku: Config Vars (encrypted at rest)
- Docker: Docker Secrets or external secret management
Non-Sensitive Values
Port numbers, feature flags, public URLs:
- Safe to include in
.env.example - Can have defaults in source code
Required vs Optional Variables
Required Variables (no safe default):
const apiKey = process.env.API_KEY;
if (!apiKey) {
console.error('ERROR: API_KEY is required but not set');
process.exit(1);
}
Optional Variables (with safe defaults):
const port = process.env.PORT || 3000;
const logLevel = process.env.LOG_LEVEL || 'info';
Platform-Specific Considerations
Google Cloud Run
- PORT: Automatically assigned by Cloud Run (typically 8080). DO NOT hardcode. Application must read from
process.env.PORT. - Secrets: Use Secret Manager, reference in
gcloud run deploywith--set-secrets
AWS Elastic Beanstalk
- PORT: Can be set via environment properties, but application must read from
process.env.PORT - Secrets: Use AWS Secrets Manager, reference in
.ebextensionsor set via console
AWS ECS/Fargate
- Environment Variables: Set in task definition under
containerDefinitions[].environment - Secrets: Use
secretsfield in task definition to pull from Secrets Manager or Parameter Store
Heroku
- PORT: Automatically assigned by Heroku. Application must read from
process.env.PORT - Config Vars: Set via
heroku config:set VARIABLE=valueor dashboard
Docker / Docker Compose
- Environment Variables: Set in
docker-compose.ymlunderenvironmentor via.envfile - Secrets: Use Docker Secrets for swarm mode, or mount secret files as volumes
Common Patterns
Feature Flags
const featureEnabled = process.env.FEATURE_NAME === 'true';
if (featureEnabled) {
// Feature-specific code
}
API Configuration
const apiBaseUrl = process.env.API_BASE_URL || 'https://api.example.com';
const apiTimeout = parseInt(process.env.API_TIMEOUT || '5000', 10);
Database Configuration
const dbConfig = {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432', 10),
database: process.env.DB_NAME || 'myapp',
user: process.env.DB_USER,
password: process.env.DB_PASSWORD
};
// Validate required fields
if (!dbConfig.user || !dbConfig.password) {
console.error('ERROR: DB_USER and DB_PASSWORD must be set');
process.exit(1);
}
Multi-Environment Configuration
const isDevelopment = process.env.NODE_ENV === 'development';
const isProduction = process.env.NODE_ENV === 'production';
const isTest = process.env.NODE_ENV === 'test';
if (isDevelopment) {
// Development-specific settings
}
Testing Environment Variables
Local Testing Without .env
# Test that defaults work
npm start
# Verify application starts successfully
# Check that default values are used
Local Testing With .env
# Create .env file
cp .env.example .env
# Edit .env with test values
# Run application
npm start
# Verify variables are read correctly
Testing Required Variables
# Test that application fails gracefully when required var is missing
# Remove required variable from .env
npm start
# Should see error message and exit with code 1
Documentation Standards
.env.example Format
# ============================================
# Server Configuration
# ============================================
# Server port
# Default: 3000
PORT=3000
# ============================================
# Application Settings
# ============================================
# Node environment (development, production, test)
# Default: (unset)
NODE_ENV=production
# ============================================
# API Configuration
# ============================================
# External API base URL
# Required: Yes
API_BASE_URL=https://api.example.com
# API timeout in milliseconds
# Default: 5000
API_TIMEOUT=5000
ENVIRONMENT-VARIABLES.md Format
Follow the existing structure in docs/ENVIRONMENT-VARIABLES.md:
- Currently Supported Variables (list with descriptions)
- Local Development Setup (how to set variables)
- Platform-Specific Setup (AWS, GCP, Heroku, Docker)
- Security Best Practices
- Troubleshooting
Troubleshooting
Variable Not Being Read
- Check spelling in
.envfile matches code exactly - Verify
.envfile is in project root (same directory asserver.js) - Restart server after changing
.envfile - Check for syntax errors in
.env(no quotes needed, no spaces around=) - Verify
dotenvpackage is installed if using:npm install dotenv
Variable Not Available in Docker
- Check variable is defined in
docker-compose.ymlenvironment section - Rebuild container:
docker compose up --build - Check variable in running container:
docker exec container_name env | grep VARIABLE
Wrong Default Value Used
- Verify fallback logic:
process.env.VAR || 'default' - Check for empty string:
process.env.VAR || 'default'(empty string is falsy) - For numbers, use:
parseInt(process.env.VAR || '100', 10)
Current Environment Variables
Reference: See docs/ENVIRONMENT-VARIABLES.md for the authoritative list.
Currently Configured:
PORT- Server port (default: 3000)NODE_ENV- Node environment (default: unset)
Best Practices Summary
- Always update
.env.example- Never commit actual.env - Document everything - Update docs/ENVIRONMENT-VARIABLES.md with full details
- Use descriptive names -
API_TIMEOUTnotTIMEOUT,DB_HOSTnotHOST - Provide safe defaults - Unless the variable is truly required
- Validate required variables - Exit with clear error message if missing
- Never hardcode secrets - Use environment variables and secret managers
- Test both scenarios - With and without
.envfile - Keep documentation in sync - Update all files when adding variables
- Use platform secrets - Don't put sensitive data in environment variables in production
- Follow naming conventions - UPPER_CASE_WITH_UNDERSCORES
Quick Reference Commands
# Create .env from example
cp .env.example .env
# View current environment variables (local)
printenv | grep -E '(PORT|NODE_ENV)'
# Set variable temporarily (bash/zsh)
export VARIABLE_NAME=value
npm start
# Set variable inline (bash/zsh)
VARIABLE_NAME=value npm start
# Docker Compose with custom env file
docker compose --env-file .env.production up
# Check variable in Docker container
docker exec usa-spending-search env | grep VARIABLE_NAME
# AWS EB set variable
eb setenv VARIABLE_NAME=value
# Heroku set variable
heroku config:set VARIABLE_NAME=value
# GCP Cloud Run set variable
gcloud run services update SERVICE_NAME --set-env-vars="VARIABLE_NAME=value"
Resources
- Project Documentation:
docs/ENVIRONMENT-VARIABLES.md - Deployment Guide:
docs/DEPLOYMENT.md - Quick Start Guide:
docs/quick-start-guide.md - Example Template:
.env.example
