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

  1. Navigate to your project in Vercel dashboard
  2. Go to Settings > Environment Variables
  3. Add key-value pairs for each environment
  4. 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