Performance & Optimization

Performance optimization, monitoring, and scalability strategies for Smart Shelf deployment.

Performance & Optimization

This section covers performance optimization strategies, monitoring techniques, and scalability approaches for Smart Shelf in production.

Build Optimization

Next.js Configuration

// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  // Build optimization
  swcMinify: true,
  compress: true,
  
  // Bundle analysis
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        ...config.resolve.fallback,
        fs: false,
      };
    }
    
    // Bundle analyzer in development
    if (process.env.ANALYZE === 'true') {
      const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
      config.plugins.push(
        new BundleAnalyzerPlugin({
          analyzerMode: 'static',
          openAnalyzer: false,
        })
      );
    }
    
    return config;
  },
  
  // Image optimization
  images: {
    domains: ['supabase-storage-url.com'],
    formats: ['image/webp', 'image/avif'],
    minimumCacheTTL: 60 * 60 * 24 * 30, // 30 days
  },
  
  // Headers for caching
  async headers() {
    return [
      {
        source: '/api/:path*',
        headers: [
          { key: 'Cache-Control', value: 'no-store, must-revalidate' },
        ],
      },
      {
        source: '/_next/static/:path*',
        headers: [
          { key: 'Cache-Control', value: 'public, max-age=31536000, immutable' },
        ],
      },
    ];
  },
  
  // Experimental features
  experimental: {
    serverComponentsExternalPackages: ['@supabase/supabase-js'],
    optimizePackageImports: ['lucide-react', '@radix-ui/react-icons'],
  },
};

export default nextConfig;

Bundle Analysis and Optimization

// scripts/analyze-bundle.ts
import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

async function analyzeBundles() {
  console.log('📊 Analyzing bundle sizes...');
  
  try {
    // Build with bundle analyzer
    await execAsync('ANALYZE=true npm run build');
    
    // Check bundle sizes
    const { stdout } = await execAsync('npx next-bundle-analyzer');
    
    console.log('Bundle analysis complete:', stdout);
    
    // Check for large bundles
    const largeBundles = checkBundleSizes();
    if (largeBundles.length > 0) {
      console.warn('⚠️ Large bundles detected:', largeBundles);
    }
    
  } catch (error) {
    console.error('Bundle analysis failed:', error);
    process.exit(1);
  }
}

function checkBundleSizes() {
  // Implementation to check bundle sizes and warn about large bundles
  const MAX_BUNDLE_SIZE = 1024 * 1024; // 1MB
  const largeBundles: string[] = [];
  
  // Check each bundle against threshold
  // Return array of bundles exceeding limit
  
  return largeBundles;
}

if (require.main === module) {
  analyzeBundles();
}

Database Performance

Query Optimization

-- Performance indexes for common queries
CREATE INDEX CONCURRENTLY idx_inventory_product_warehouse 
ON inventory(product_id, warehouse_id);

CREATE INDEX CONCURRENTLY idx_stock_movements_product_date 
ON stock_movements(product_id, created_at DESC);

CREATE INDEX CONCURRENTLY idx_products_search 
ON products USING gin(to_tsvector('english', name || ' ' || description));

-- Partial indexes for active records
CREATE INDEX CONCURRENTLY idx_products_active 
ON products(name) WHERE is_active = true;

CREATE INDEX CONCURRENTLY idx_inventory_available 
ON inventory(product_id, quantity_available) WHERE quantity_available > 0;

-- Composite indexes for complex queries
CREATE INDEX CONCURRENTLY idx_orders_status_date 
ON sales_orders(status, created_at DESC);

-- Enable query plan caching
ALTER DATABASE smart_shelf SET shared_preload_libraries = 'pg_stat_statements';
ALTER DATABASE smart_shelf SET pg_stat_statements.track = 'all';

Connection Pool Optimization

// lib/supabase/performance.ts
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;

// Optimized connection configuration
export const createOptimizedClient = () => {
  return createClient(supabaseUrl, supabaseKey, {
    db: {
      schema: 'public',
    },
    auth: {
      autoRefreshToken: true,
      persistSession: true,
      detectSessionInUrl: false,
    },
    global: {
      headers: {
        'x-application-name': 'smart-shelf',
      },
    },
    // Connection pooling settings
    realtime: {
      params: {
        eventsPerSecond: 10,
      },
    },
  });
};

// Query performance tracking
export async function trackQuery<T>(
  queryName: string,
  queryFn: () => Promise<T>
): Promise<T> {
  const start = performance.now();
  
  try {
    const result = await queryFn();
    const duration = performance.now() - start;
    
    // Log slow queries (> 1 second)
    if (duration > 1000) {
      console.warn(`🐌 Slow query detected: ${queryName} took ${duration.toFixed(2)}ms`);
    }
    
    // Track metrics in production
    if (process.env.NODE_ENV === 'production') {
      await logPerformanceMetric({
        type: 'database_query',
        name: queryName,
        duration,
        timestamp: new Date(),
      });
    }
    
    return result;
  } catch (error) {
    const duration = performance.now() - start;
    console.error(`❌ Query failed: ${queryName} after ${duration.toFixed(2)}ms`, error);
    throw error;
  }
}

Performance Monitoring

Real User Monitoring (RUM)

// lib/monitoring/performance.ts
import { ENV } from '@/lib/config/environment';

// Performance monitoring
export function trackPerformance(name: string, fn: () => Promise<any>) {
  return async function(...args: any[]) {
    const start = performance.now();
    
    try {
      const result = await fn.apply(this, args);
      
      const duration = performance.now() - start;
      
      // Log performance metrics
      if (ENV.NODE_ENV === 'production') {
        await fetch('/api/metrics/performance', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            name,
            duration,
            timestamp: new Date().toISOString(),
          }),
        });
      }
      
      return result;
    } catch (error) {
      // Track errors
      if (ENV.NODE_ENV === 'production') {
        await fetch('/api/metrics/errors', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            name,
            error: error.message,
            stack: error.stack,
            timestamp: new Date().toISOString(),
          }),
        });
      }
      
      throw error;
    }
  };
}

// Real User Monitoring (RUM)
export function initRUM() {
  if (typeof window !== 'undefined' && ENV.NODE_ENV === 'production') {
    // Track Core Web Vitals
    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
      getCLS(sendMetric);
      getFID(sendMetric);
      getFCP(sendMetric);
      getLCP(sendMetric);
      getTTFB(sendMetric);
    });
  }
}

function sendMetric(metric: any) {
  fetch('/api/metrics/web-vitals', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(metric),
  });
}

// Performance budget monitoring
export const PERFORMANCE_BUDGETS = {
  FCP: 1.8, // First Contentful Paint
  LCP: 2.5, // Largest Contentful Paint
  FID: 0.1, // First Input Delay
  CLS: 0.1, // Cumulative Layout Shift
  TTFB: 0.8, // Time to First Byte
};

export function checkPerformanceBudgets(metrics: Record<string, number>) {
  const violations = [];
  
  for (const [metric, value] of Object.entries(metrics)) {
    const budget = PERFORMANCE_BUDGETS[metric as keyof typeof PERFORMANCE_BUDGETS];
    if (budget && value > budget) {
      violations.push({
        metric,
        value,
        budget,
        exceeded: value - budget,
      });
    }
  }
  
  return violations;
}

Health Checks and Monitoring

// app/api/health/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { createServiceRoleClient } from '@/lib/supabase/server';

export async function GET(request: NextRequest) {
  const checks = [];
  
  // Database health check
  try {
    const supabase = createServiceRoleClient();
    const start = performance.now();
    
    const { data, error } = await supabase
      .from('health_check')
      .select('count')
      .limit(1);
    
    const responseTime = performance.now() - start;
    
    checks.push({
      name: 'database',
      status: error ? 'unhealthy' : 'healthy',
      responseTime: Math.round(responseTime),
      details: error ? { error: error.message } : null,
    });
  } catch (error) {
    checks.push({
      name: 'database',
      status: 'unhealthy',
      error: error.message,
    });
  }
  
  // External services health check
  try {
    const start = performance.now();
    const response = await fetch('https://api.stripe.com/v1/account', {
      headers: { Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}` },
    });
    const responseTime = performance.now() - start;
    
    checks.push({
      name: 'stripe',
      status: response.ok ? 'healthy' : 'unhealthy',
      responseTime: Math.round(responseTime),
    });
  } catch (error) {
    checks.push({
      name: 'stripe',
      status: 'unhealthy',
      error: error.message,
    });
  }
  
  // Memory usage check
  if (process.memoryUsage) {
    const memUsage = process.memoryUsage();
    checks.push({
      name: 'memory',
      status: memUsage.heapUsed < 512 * 1024 * 1024 ? 'healthy' : 'warning', // 512MB threshold
      details: {
        heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024),
        heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024),
        external: Math.round(memUsage.external / 1024 / 1024),
      },
    });
  }
  
  const allHealthy = checks.every(check => check.status === 'healthy');
  
  return NextResponse.json(
    {
      status: allHealthy ? 'healthy' : 'unhealthy',
      timestamp: new Date().toISOString(),
      version: process.env.NEXT_PUBLIC_APP_VERSION,
      checks,
    },
    { status: allHealthy ? 200 : 503 }
  );
}

Caching Strategies

Application-Level Caching

// lib/cache/redis.ts
import { Redis } from 'ioredis';

const redis = new Redis(process.env.REDIS_URL!);

export interface CacheOptions {
  ttl?: number; // Time to live in seconds
  prefix?: string;
}

export class CacheManager {
  private defaultTTL = 3600; // 1 hour
  
  async get<T>(key: string, options?: CacheOptions): Promise<T | null> {
    try {
      const fullKey = this.buildKey(key, options?.prefix);
      const value = await redis.get(fullKey);
      
      return value ? JSON.parse(value) : null;
    } catch (error) {
      console.error('Cache get error:', error);
      return null;
    }
  }
  
  async set<T>(key: string, value: T, options?: CacheOptions): Promise<void> {
    try {
      const fullKey = this.buildKey(key, options?.prefix);
      const ttl = options?.ttl || this.defaultTTL;
      
      await redis.setex(fullKey, ttl, JSON.stringify(value));
    } catch (error) {
      console.error('Cache set error:', error);
    }
  }
  
  async del(key: string, options?: CacheOptions): Promise<void> {
    try {
      const fullKey = this.buildKey(key, options?.prefix);
      await redis.del(fullKey);
    } catch (error) {
      console.error('Cache delete error:', error);
    }
  }
  
  async invalidatePattern(pattern: string): Promise<void> {
    try {
      const keys = await redis.keys(pattern);
      if (keys.length > 0) {
        await redis.del(...keys);
      }
    } catch (error) {
      console.error('Cache invalidation error:', error);
    }
  }
  
  private buildKey(key: string, prefix?: string): string {
    return prefix ? `${prefix}:${key}` : key;
  }
}

export const cache = new CacheManager();

// Cache decorator for functions
export function cached<T extends (...args: any[]) => Promise<any>>(
  fn: T,
  options?: CacheOptions & { keyGenerator?: (...args: Parameters<T>) => string }
): T {
  return (async (...args: Parameters<T>) => {
    const key = options?.keyGenerator
      ? options.keyGenerator(...args)
      : `${fn.name}:${JSON.stringify(args)}`;
    
    // Try to get from cache
    const cached = await cache.get(key, options);
    if (cached) return cached;
    
    // Execute function and cache result
    const result = await fn(...args);
    await cache.set(key, result, options);
    
    return result;
  }) as T;
}

Database Query Caching

// lib/cache/query-cache.ts
import { cache } from './redis';

export function cacheQuery<T>(
  queryName: string,
  ttl: number = 300 // 5 minutes default
) {
  return function(queryFn: () => Promise<T>): Promise<T> {
    return cached(queryFn, {
      prefix: 'query',
      ttl,
      keyGenerator: () => queryName,
    })();
  };
}

// Usage example
export const getProductsWithCache = () =>
  cacheQuery('products:active', 600)(async () => {
    const supabase = createOptimizedClient();
    const { data } = await supabase
      .from('products')
      .select('*')
      .eq('is_active', true);
    return data;
  });

Image and Asset Optimization

Image Processing Pipeline

// lib/image/optimization.ts
import sharp from 'sharp';

export interface ImageOptimizationOptions {
  width?: number;
  height?: number;
  quality?: number;
  format?: 'webp' | 'avif' | 'jpeg' | 'png';
}

export async function optimizeImage(
  input: Buffer,
  options: ImageOptimizationOptions = {}
): Promise<Buffer> {
  const {
    width,
    height,
    quality = 80,
    format = 'webp',
  } = options;
  
  let pipeline = sharp(input);
  
  // Resize if dimensions provided
  if (width || height) {
    pipeline = pipeline.resize(width, height, {
      fit: 'cover',
      position: 'center',
    });
  }
  
  // Apply format-specific optimizations
  switch (format) {
    case 'webp':
      pipeline = pipeline.webp({ quality });
      break;
    case 'avif':
      pipeline = pipeline.avif({ quality });
      break;
    case 'jpeg':
      pipeline = pipeline.jpeg({ quality, progressive: true });
      break;
    case 'png':
      pipeline = pipeline.png({ compressionLevel: 9 });
      break;
  }
  
  return pipeline.toBuffer();
}

// Image variants generation
export async function generateImageVariants(
  originalBuffer: Buffer,
  variants: ImageOptimizationOptions[]
): Promise<Record<string, Buffer>> {
  const results: Record<string, Buffer> = {};
  
  await Promise.all(
    variants.map(async (variant, index) => {
      const key = `variant-${index}`;
      results[key] = await optimizeImage(originalBuffer, variant);
    })
  );
  
  return results;
}

Performance Best Practices

Code Splitting

// components/LazyComponents.tsx
import { lazy, Suspense } from 'react';
import { Skeleton } from '@/components/ui/skeleton';

// Lazy load heavy components
const ProductCatalog = lazy(() => import('./ProductCatalog'));
const InventoryDashboard = lazy(() => import('./InventoryDashboard'));
const ReportsPanel = lazy(() => import('./ReportsPanel'));

export function LazyProductCatalog() {
  return (
    <Suspense fallback={<ProductCatalogSkeleton />}>
      <ProductCatalog />
    </Suspense>
  );
}

export function LazyInventoryDashboard() {
  return (
    <Suspense fallback={<DashboardSkeleton />}>
      <InventoryDashboard />
    </Suspense>
  );
}

function ProductCatalogSkeleton() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
      {Array.from({ length: 6 }).map((_, i) => (
        <Skeleton key={i} className="h-48 w-full" />
      ))}
    </div>
  );
}

Resource Loading

// lib/performance/resource-hints.ts
export function preloadCriticalResources() {
  if (typeof window === 'undefined') return;
  
  // Preload critical fonts
  const fontLink = document.createElement('link');
  fontLink.rel = 'preload';
  fontLink.href = '/fonts/inter-var.woff2';
  fontLink.as = 'font';
  fontLink.type = 'font/woff2';
  fontLink.crossOrigin = 'anonymous';
  document.head.appendChild(fontLink);
  
  // Prefetch likely next pages
  const prefetchLinks = [
    '/dashboard',
    '/products',
    '/inventory',
  ];
  
  prefetchLinks.forEach(href => {
    const link = document.createElement('link');
    link.rel = 'prefetch';
    link.href = href;
    document.head.appendChild(link);
  });
}

export function preconnectToExternalDomains() {
  const domains = [
    'https://fonts.googleapis.com',
    'https://cdn.jsdelivr.net',
    process.env.NEXT_PUBLIC_SUPABASE_URL,
  ];
  
  domains.forEach(domain => {
    const link = document.createElement('link');
    link.rel = 'preconnect';
    link.href = domain;
    document.head.appendChild(link);
  });
}

Performance Checklist

Development

  • Bundle analysis configured
  • Code splitting implemented
  • Lazy loading for heavy components
  • Image optimization enabled
  • Caching strategies in place

Database

  • Proper indexes created
  • Query performance monitored
  • Connection pooling optimized
  • Slow query logging enabled

Monitoring

  • Core Web Vitals tracked
  • Performance budgets defined
  • Health checks implemented
  • Real user monitoring active
  • Error tracking configured

This performance optimization strategy ensures Smart Shelf delivers excellent user experience with fast loading times and responsive interactions.