The QuickLendX backend uses a production-grade configuration system with strict validation, fail-fast behavior, and automatic secret redaction. The system ensures the application cannot start with invalid or missing environment variables.
- Strict Type Validation: Uses Zod schema validation to enforce types and constraints
- Fail-Fast Behavior: Application terminates immediately if configuration is invalid
- Profile Management: Supports development, test, and production environments with profile-specific rules
- Secret Redaction: Automatically masks sensitive values in logs and error messages
- Comprehensive Error Messages: Clear, actionable errors without exposing sensitive data
- Relaxed validation rules
- Shorter minimum lengths for secrets (32 characters)
- Allows any database type
- Verbose logging enabled
- Similar to development
- Minimal console output
- Optimized for CI/CD pipelines
- Stricter validation rules
- Longer minimum lengths for secrets (64 characters)
- Requires PostgreSQL database
- Enhanced security checks
| Variable | Type | Required | Default | Description | Production Rules |
|---|---|---|---|---|---|
NODE_ENV |
enum | No | development |
Environment profile: development, test, production | - |
PORT |
integer | No | 3000 |
Server port (1-65535) | - |
LOG_LEVEL |
enum | No | info |
Log level: debug, info, warn, error | - |
DATABASE_URL |
URL | Yes | - | Database connection string | Must be PostgreSQL |
DATABASE_POOL_SIZE |
integer | No | 10 |
Connection pool size (1-100) | - |
JWT_SECRET |
string | Yes | - | JWT signing secret | Min 64 chars (vs 32 in dev) |
API_KEY |
string | Yes | - | API authentication key | Min 32 chars (vs 16 in dev) |
ENCRYPTION_KEY |
string | Yes | - | Data encryption key | Min 64 chars (vs 32 in dev) |
STELLAR_NETWORK_URL |
URL | Yes | - | Stellar Horizon API URL | - |
STELLAR_NETWORK_PASSPHRASE |
string | Yes | - | Stellar network passphrase | - |
ENABLE_RATE_LIMITING |
boolean | No | true |
Enable API rate limiting | - |
MAX_REQUESTS_PER_MINUTE |
integer | No | 100 |
Rate limit threshold (1-10000) | - |
SENTRY_DSN |
URL | No | - | Sentry error tracking DSN | - |
import { getConfig } from './config';
// Get configuration (loads automatically on first call)
const config = getConfig();
console.log(`Server starting on port ${config.PORT}`);
console.log(`Environment: ${config.NODE_ENV}`);import { getSafeConfig, formatSafeConfig } from './config';
const config = getConfig();
// Get safe version with secrets redacted
const safeConfig = getSafeConfig(config);
console.log('Config:', safeConfig);
// Output: { PORT: 3000, JWT_SECRET: '[REDACTED]', ... }
// Format as JSON string
console.log(formatSafeConfig(config));import { resetConfig, getConfig } from './config';
// Reset singleton instance
resetConfig();
// Next call will reload configuration
const newConfig = getConfig();The system loads environment variables from multiple sources in order:
.env- Base configuration.env.{profile}- Profile-specific (e.g.,.env.production).env.{profile}.local- Local overrides (gitignored)process.env- System environment variables
Later sources override earlier ones.
# Application
NODE_ENV=development
PORT=3000
LOG_LEVEL=debug
# Database
DATABASE_URL=postgresql://localhost:5432/quicklendx_dev
DATABASE_POOL_SIZE=10
# Security (NEVER commit real secrets!)
JWT_SECRET=development-jwt-secret-minimum-32-characters-long-for-security
API_KEY=dev-api-key-1234
ENCRYPTION_KEY=development-encryption-key-minimum-32-characters-required
# Stellar Network
STELLAR_NETWORK_URL=https://horizon-testnet.stellar.org
STELLAR_NETWORK_PASSPHRASE=Test SDF Network ; September 2015
# Features
ENABLE_RATE_LIMITING=true
MAX_REQUESTS_PER_MINUTE=100
# Monitoring (optional)
# SENTRY_DSN=https://your-sentry-dsn@sentry.io/projectNODE_ENV=production
# Use longer secrets in production
JWT_SECRET=${JWT_SECRET} # From environment, min 64 chars
API_KEY=${API_KEY} # From environment, min 32 chars
ENCRYPTION_KEY=${ENCRYPTION_KEY} # From environment, min 64 chars
# Production database
DATABASE_URL=${DATABASE_URL} # Must be PostgreSQL
DATABASE_POOL_SIZE=50
# Production Stellar
STELLAR_NETWORK_URL=https://horizon.stellar.org
STELLAR_NETWORK_PASSPHRASE=Public Global Stellar Network ; September 2015
# Monitoring
SENTRY_DSN=${SENTRY_DSN}Edit src/config/schema.ts:
export const ConfigSchema = z.object({
// ... existing fields ...
// Add your new field
NEW_VARIABLE: z.string().min(1),
NEW_NUMBER: z.coerce.number().int().min(0).default(100),
});export const ProductionConfigSchema = ConfigSchema.extend({
// Override with stricter rules for production
NEW_VARIABLE: z.string().min(10),
});Add the new variable to the table above with:
- Variable name
- Type
- Required/Optional
- Default value
- Description
- Production-specific rules
# Add to .env.example
NEW_VARIABLE=example-value
NEW_NUMBER=100The system automatically identifies sensitive keys using patterns:
passwordsecrettokenkeyauthcredentialprivateapi_key/api-key
These values are automatically redacted in:
- Console logs
- Error messages
- String representations
- Debug output
const config = {
PORT: 3000,
JWT_SECRET: 'super-secret-value',
DATABASE_PASSWORD: 'db-password-123',
};
console.log(getSafeConfig(config));
// Output:
// {
// PORT: 3000,
// JWT_SECRET: '[REDACTED]',
// DATABASE_PASSWORD: '[REDACTED]'
// }When validation fails, error messages never include the actual values of sensitive fields:
❌ CONFIGURATION ERROR
Configuration validation failed for profile "production":
- JWT_SECRET: String must contain at least 64 character(s)
- API_KEY: String must contain at least 32 character(s)
Please check your environment variables and try again.
Note: The actual secret values are NOT shown in the error.
- DATABASE_URL: Required
Solution: Add the variable to your .env file
- PORT: Expected number, received string
Solution: Ensure the value is a valid number (no quotes needed in .env)
- DATABASE_URL: Invalid url
Solution: Ensure the URL is properly formatted (e.g., postgresql://host:port/db)
- PORT: Number must be greater than or equal to 1
Solution: Provide a value within the allowed range
- NODE_ENV: Invalid enum value. Expected 'development' | 'test' | 'production'
Solution: Use one of the allowed values
- JWT_SECRET: String must contain at least 64 character(s)
Solution: Use a longer secret in production (generate with openssl rand -base64 64)
- DATABASE_URL: Production database must use PostgreSQL
Solution: Use a PostgreSQL connection string (starts with postgresql:// or postgres://)
npm test src/confignpm run test:coverage -- src/configTarget: 95%+ coverage
# Development
NODE_ENV=development npm test
# Production validation
NODE_ENV=production npm test
# Test profile
NODE_ENV=test npm test- name: Validate Configuration
env:
NODE_ENV: production
DATABASE_URL: ${{ secrets.DATABASE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
API_KEY: ${{ secrets.API_KEY }}
ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }}
STELLAR_NETWORK_URL: https://horizon.stellar.org
STELLAR_NETWORK_PASSPHRASE: Public Global Stellar Network ; September 2015
run: |
npm run build
node -e "require('./dist/config').getConfig()"- Check console output for validation errors
- Verify all required variables are set
- Ensure values match expected types and formats
- Check production-specific rules if
NODE_ENV=production
This should never happen. If it does:
- Check if the key name matches sensitive patterns
- Verify you're using
getSafeConfig()for logging - Report as a security issue
Environment variables are always strings. Use z.coerce.number() or z.coerce.boolean() for automatic conversion.
- Never commit secrets - Use
.env.localfor local secrets (gitignored) - Use environment variables in production - Don't use .env files in production
- Generate strong secrets - Use
openssl rand -base64 64for production secrets - Validate early - Configuration is validated at startup, before any other code runs
- Log safely - Always use
getSafeConfig()when logging configuration - Document new variables - Update this file when adding configuration options
- Test with production profile - Ensure your configuration passes production validation
✅ No secrets in logs: All sensitive values automatically redacted
✅ No secrets in errors: Validation errors never expose secret values
✅ Fail-fast: Invalid configuration prevents application startup
✅ Type safety: Zod ensures runtime type correctness
✅ Production hardening: Stricter rules for production environment
Last Updated: 2024-01-25