Environment Configuration
Configure environment variables and settings for different deployment environments.
Environment Configuration
Comprehensive guide for configuring environment variables and settings across development, staging, and production environments.
Environment Variables
Core Application Variables
Required Variables
These variables are essential for the application to function:
# Application Configuration
NODE_ENV=production
NEXT_PUBLIC_APP_URL=https://your-domain.com
NEXT_PUBLIC_APP_NAME="Smart Shelf"
# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# Database
DATABASE_URL=postgresql://user:pass@host:port/db
Optional Variables
Additional variables for enhanced functionality:
# Analytics
NEXT_PUBLIC_GA_ID=GA_MEASUREMENT_ID
VERCEL_ANALYTICS_ID=your-analytics-id
NEXT_PUBLIC_HOTJAR_ID=your-hotjar-id
# Email Configuration
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
SMTP_FROM=noreply@smartshelf.app
# File Storage (AWS S3)
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1
AWS_S3_BUCKET=smartshelf-uploads
# External Services
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...
SENDGRID_API_KEY=SG....
TWILIO_ACCOUNT_SID=AC...
TWILIO_AUTH_TOKEN=...
# Security
JWT_SECRET=your-jwt-secret
ENCRYPTION_KEY=your-32-character-encryption-key
NEXTAUTH_SECRET=your-nextauth-secret
Environment-Specific Configuration
Development Environment
# Development settings
NODE_ENV=development
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_TELEMETRY_DISABLED=1
# Local Supabase
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-local-anon-key
# Debug settings
DEBUG=true
LOG_LEVEL=debug
ENABLE_MOCK_DATA=true
Staging Environment
# Staging settings
NODE_ENV=staging
NEXT_PUBLIC_APP_URL=https://staging.smartshelf.app
# Staging database
DATABASE_URL=postgresql://staging_user:pass@staging-host:5432/smartshelf_staging
# Testing configurations
ENABLE_TEST_ROUTES=true
DISABLE_RATE_LIMITING=true
MOCK_EXTERNAL_SERVICES=true
Production Environment
# Production settings
NODE_ENV=production
NEXT_PUBLIC_APP_URL=https://smartshelf.app
# Security enhancements
SECURE_COOKIES=true
ENABLE_RATE_LIMITING=true
LOG_LEVEL=error
SENTRY_DSN=https://your-sentry-dsn
# Performance settings
ENABLE_CACHING=true
CDN_URL=https://cdn.smartshelf.app
Configuration Management
Vercel Environment Variables
Setting Variables via CLI
# Add production variable
vercel env add NEXT_PUBLIC_SUPABASE_URL production
# Enter value when prompted
# Add to all environments
vercel env add JWT_SECRET development staging production
# Import from file
vercel env add < .env.production
Setting Variables via Dashboard
- Navigate to your project in Vercel dashboard
- Go to Settings > Environment Variables
- Add key-value pairs for each environment
- Select appropriate environments (Development, Preview, Production)
Environment Files
Local Development (.env.local)
# .env.local (for local development only)
NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-local-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-local-service-role-key
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/smartshelf
Template File (.env.example)
# .env.example (committed to repository)
NEXT_PUBLIC_APP_URL=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
DATABASE_URL=
JWT_SECRET=
SMTP_HOST=
SMTP_USER=
SMTP_PASS=
Configuration Validation
Environment Schema Validation
// lib/env.ts
import { z } from 'zod'
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'staging', 'production']),
NEXT_PUBLIC_APP_URL: z.string().url(),
NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
SMTP_HOST: z.string().optional(),
SMTP_PORT: z.string().optional(),
SMTP_USER: z.string().optional(),
SMTP_PASS: z.string().optional(),
})
export const env = envSchema.parse(process.env)
Runtime Configuration Check
// lib/config.ts
export function validateConfig() {
const required = [
'NEXT_PUBLIC_SUPABASE_URL',
'NEXT_PUBLIC_SUPABASE_ANON_KEY',
'SUPABASE_SERVICE_ROLE_KEY',
]
const missing = required.filter(key => !process.env[key])
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`)
}
}
// Call during app initialization
validateConfig()
Platform-Specific Configuration
Vercel Configuration
vercel.json
{
"version": 2,
"build": {
"env": {
"NODE_ENV": "production"
}
},
"functions": {
"app/**/*.ts": {
"runtime": "@vercel/node",
"maxDuration": 30
}
},
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
},
{
"key": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
}
]
}
],
"redirects": [
{
"source": "/admin",
"destination": "/dashboard",
"permanent": false
}
],
"rewrites": [
{
"source": "/api/(.*)",
"destination": "/api/$1"
}
]
}
Netlify Configuration
netlify.toml
[build]
publish = ".next"
command = "npm run build"
[build.environment]
NODE_VERSION = "18"
NPM_VERSION = "9"
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
[[redirects]]
from = "/admin"
to = "/dashboard"
status = 302
[context.production.environment]
NODE_ENV = "production"
[context.deploy-preview.environment]
NODE_ENV = "staging"
Railway Configuration
railway.json
{
"build": {
"builder": "NIXPACKS",
"buildCommand": "npm run build"
},
"deploy": {
"startCommand": "npm start",
"healthcheckPath": "/api/health",
"healthcheckTimeout": 300,
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 10
}
}
Feature Flags
Feature Flag Configuration
// lib/features.ts
export const features = {
enableAnalytics: process.env.NODE_ENV === 'production',
enableBetaFeatures: process.env.ENABLE_BETA_FEATURES === 'true',
enableMockData: process.env.NODE_ENV === 'development',
maxUploadSize: parseInt(process.env.MAX_UPLOAD_SIZE || '10485760'), // 10MB
rateLimit: {
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW || '900000'), // 15 minutes
maxRequests: parseInt(process.env.RATE_LIMIT_MAX || '100'),
},
cache: {
ttl: parseInt(process.env.CACHE_TTL || '300'), // 5 minutes
enabled: process.env.ENABLE_CACHING !== 'false',
},
}
// Usage in components
export function AnalyticsWidget() {
if (!features.enableAnalytics) return null
return <div>Analytics content</div>
}
Environment-Based Features
// lib/env-features.ts
interface EnvironmentFeatures {
development: {
enableDevTools: boolean
enableMockData: boolean
enableTestRoutes: boolean
}
staging: {
enableTestRoutes: boolean
enablePerformanceDebugging: boolean
}
production: {
enableAnalytics: boolean
enableErrorReporting: boolean
enablePerformanceMonitoring: boolean
}
}
export const environmentFeatures: EnvironmentFeatures = {
development: {
enableDevTools: true,
enableMockData: true,
enableTestRoutes: true,
},
staging: {
enableTestRoutes: true,
enablePerformanceDebugging: true,
},
production: {
enableAnalytics: true,
enableErrorReporting: true,
enablePerformanceMonitoring: true,
},
}
export function getFeatures() {
const env = process.env.NODE_ENV as keyof EnvironmentFeatures
return environmentFeatures[env] || environmentFeatures.development
}
Security Considerations
Sensitive Data Protection
Client-Side vs Server-Side Variables
// ✅ Safe for client-side (NEXT_PUBLIC_ prefix)
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
// ❌ Server-side only (no NEXT_PUBLIC_ prefix)
const serviceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY
// ✅ Safe usage pattern
export function getPublicConfig() {
return {
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL,
appUrl: process.env.NEXT_PUBLIC_APP_URL,
}
}
export function getServerConfig() {
return {
serviceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY,
jwtSecret: process.env.JWT_SECRET,
}
}
Secret Rotation Strategy
# Rotate secrets regularly
vercel env rm OLD_SECRET_KEY production
vercel env add NEW_SECRET_KEY production
# Database password rotation
pg_dump $OLD_DATABASE_URL > backup.sql
# Update password in database
# Update DATABASE_URL environment variable
psql $NEW_DATABASE_URL < backup.sql
Environment Isolation
Development Safety
// lib/safety-checks.ts
export function ensureDevelopmentOnly(feature: string) {
if (process.env.NODE_ENV === 'production') {
throw new Error(`${feature} is not allowed in production`)
}
}
// Usage
export async function seedDatabase() {
ensureDevelopmentOnly('Database seeding')
// Seeding logic here
}
Production Safeguards
// lib/production-guards.ts
export function ensureProduction(feature: string) {
if (process.env.NODE_ENV !== 'production') {
console.warn(`${feature} is recommended for production only`)
}
}
// Usage
export function enableAnalytics() {
ensureProduction('Analytics tracking')
// Analytics initialization
}
Configuration Best Practices
1. Environment Separation
- Use different databases for each environment
- Separate API keys and credentials
- Implement feature flags for environment-specific behavior
2. Secret Management
- Never commit secrets to version control
- Use secure secret management services
- Rotate secrets regularly
- Use least-privilege access
3. Validation
- Validate all environment variables at startup
- Use schema validation libraries (Zod, Joi)
- Fail fast on missing required variables
4. Documentation
- Maintain up-to-date .env.example files
- Document required vs optional variables
- Include setup instructions for each environment
5. Monitoring
- Monitor configuration changes
- Track environment variable usage
- Alert on configuration errors
Troubleshooting
Common Issues
Missing Environment Variables
# Check current variables
vercel env ls
# Verify variable in specific environment
vercel env ls --environment production
# Test variable loading
node -e "console.log(process.env.NEXT_PUBLIC_SUPABASE_URL)"
Variable Not Loading
// Debug environment loading
console.log('Environment check:', {
nodeEnv: process.env.NODE_ENV,
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL ? '✅ Set' : '❌ Missing',
hasServiceKey: process.env.SUPABASE_SERVICE_ROLE_KEY ? '✅ Set' : '❌ Missing',
})
Vercel Environment Issues
# Force redeploy with new environment variables
vercel --force
# Check build logs for environment issues
vercel logs --follow
Related Documentation
- Production Deployment - Deployment procedures
- Security Guidelines - Security best practices
- Monitoring Setup - Observability configuration
- Performance Optimization - Performance settings