~/wiki

Error Handling Patterns

Confiance : high
error-handlingexception-patternsdefensive-programmingclient-server-boundariestimeout-handling

Comprehensive patterns for managing errors across client-server boundaries, implementing defensive programming practices, and ensuring proper error propagation in production web applications.

Exception Capture Patterns

Explicit Sentry Integration

Caught exceptions must be explicitly reported to monitoring services:

try {
  await riskyOperation();
} catch (error) {
  // Without this, Sentry won't see the error
  Sentry.captureException(error);
  return { error: 'Operation failed' };
}

Error Context Preservation

Include relevant context when capturing exceptions:

try {
  await syncOperation();
} catch (error) {
  Sentry.captureException(error, {
    tags: { operation: 'easybeer-sync' },
    extra: { userId, timestamp: Date.now() }
  });
}

Client-Server Boundary Handling

Network Error Classification

Distinguish between different types of client-side failures:

try {
  const response = await fetch('/api/sync');
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }
  const data = await response.json();
} catch (error) {
  if (error instanceof TypeError) {
    // Network connectivity issue
    setError('Erreur de connexion - vérifiez votre réseau');
  } else if (error.message.includes('HTTP 5')) {
    // Server error
    setError('Erreur serveur - réessayez dans quelques instants');
  } else {
    // JSON parsing or other client error
    setError('Erreur inattendue - contactez le support');
  }
}

Timeout Handling

Implement explicit timeouts for long-running operations:

const SYNC_TIMEOUT = 300000; // 5 minutes

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), SYNC_TIMEOUT);

try {
  const response = await fetch('/api/sync', {
    signal: controller.signal
  });
} catch (error) {
  if (error.name === 'AbortError') {
    setError('Synchronisation interrompue - opération trop longue');
  }
} finally {
  clearTimeout(timeoutId);
}

Defensive Programming Patterns

Array Safety

Protect against undefined array properties:

const sortedClients = useMemo(() => {
  if (!portfolio?.clients || !Array.isArray(portfolio.clients)) {
    return [];
  }
  return [...portfolio.clients].sort(/* sorting logic */);
}, [portfolio?.clients]);

Type Guard Functions

Implement runtime type checking for critical data:

function isValidPortfolio(data: unknown): data is Portfolio {
  return (
    typeof data === 'object' &&
    data !== null &&
    'clients' in data &&
    Array.isArray(data.clients)
  );
}

// Usage in component
if (!isValidPortfolio(portfolio)) {
  return <ErrorFallback message="Invalid portfolio data" />;
}

Null Coalescing Chains

Handle multiple levels of potentially undefined data:

const clientCount = portfolio?.clients?.length ?? 0;
const hasActiveClients = (portfolio?.clients ?? []).some(client => client.active);

Server-Side Error Patterns

API Route Error Handling

Consistent error responses with proper monitoring:

export async function POST(request: Request) {
  try {
    const result = await performOperation();
    return Response.json({ success: true, data: result });
  } catch (error) {
    console.error('Operation failed:', error);
    Sentry.captureException(error, {
      tags: { route: '/api/operation' }
    });
    
    return Response.json(
      { 
        success: false, 
        error: 'Operation failed',
        timestamp: new Date().toISOString()
      },
      { status: 500 }
    );
  }
}

Database Error Recovery

Implement retry logic for transient database failures:

async function retryableDbOperation<T>(
  operation: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      if (attempt === maxRetries) {
        Sentry.captureException(error, {
          tags: { operation: 'database-retry-exhausted' }
        });
        throw error;
      }
      
      // Wait before retry (exponential backoff)
      await new Promise(resolve => 
        setTimeout(resolve, Math.pow(2, attempt) * 1000)
      );
    }
  }
}

Serialization Error Patterns

Next.js Server-Client Boundaries

Handle serialization failures during prop passing:

// Server component
export default async function Page() {
  try {
    const data = await fetchData();
    const serializedData = JSON.parse(JSON.stringify(data)); // Test serialization
    return <ClientComponent data={serializedData} />;
  } catch (serializationError) {
    console.error('Serialization failed:', serializationError);
    return <ErrorFallback />;
  }
}

Date Object Handling

Prevent Date serialization issues:

// Convert Date objects to ISO strings before serialization
function serializeDates<T>(obj: T): T {
  return JSON.parse(JSON.stringify(obj, (key, value) => {
    if (value instanceof Date) {
      return value.toISOString();
    }
    return value;
  }));
}

Error Boundary Patterns

React Error Boundaries

Comprehensive error boundaries with different fallback strategies:

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }

  componentDidCatch(error, errorInfo) {
    Sentry.captureException(error, {
      contexts: { errorInfo }
    });
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || <DefaultErrorFallback />;
    }
    return this.props.children;
  }
}

Granular Error Boundaries

Implement specific error boundaries for different application sections:

// Wrap specific features to prevent cascade failures
<DashboardErrorBoundary>
  <SyncOperations />
</DashboardErrorBoundary>

<DataVisualizationErrorBoundary>
  <Charts />
</DataVisualizationErrorBoundary>

Monitoring Integration Patterns

Structured Error Logging

Consistent error logging format for monitoring:

function logError(error: Error, context: Record<string, unknown>) {
  const errorLog = {
    message: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
    context
  };
  
  console.error('Application error:', errorLog);
  Sentry.captureException(error, { extra: context });
}

Error Metrics

Track error rates and patterns:

// Custom Sentry metrics
Sentry.metrics.increment('sync.attempts');
Sentry.metrics.increment('sync.failures', 1, {
  tags: { error_type: error.constructor.name }
});

See also