Files
gridpilot.gg/apps/website/docs/API_ERROR_HANDLING.md
2025-12-31 21:24:42 +01:00

9.5 KiB

API Error Handling & Resilience System

This document describes the comprehensive error handling infrastructure added to the GridPilot website to handle API connectivity issues, request errors, and provide graceful degradation.

Overview

The system provides:

  • Enhanced error classification with detailed error types
  • Automatic retry logic with exponential backoff
  • Circuit breaker pattern to prevent cascading failures
  • Connection health monitoring with real-time status
  • User-friendly error messages for production
  • Developer-friendly debugging tools for development
  • Graceful degradation with fallbacks and caching
  • Offline mode detection

Core Components

1. Enhanced Error Classification (ApiError)

Location: lib/api/base/ApiError.ts

Error Types:

  • NETWORK_ERROR - Connection failed, CORS issues
  • AUTH_ERROR - 401/403 authentication issues
  • VALIDATION_ERROR - 400 bad request
  • NOT_FOUND - 404 resource not found
  • SERVER_ERROR - 500+ server issues
  • RATE_LIMIT_ERROR - 429 too many requests
  • TIMEOUT_ERROR - Request timeout
  • CANCELED_ERROR - Request aborted

Features:

  • Automatic error type classification
  • User-friendly vs developer-friendly messages
  • Retry capability detection
  • Connectivity issue detection
  • Severity levels for logging

2. Retry Handler & Circuit Breaker (RetryHandler.ts)

Location: lib/api/base/RetryHandler.ts

Retry Logic:

  • Exponential backoff with jitter
  • Configurable max retries (default: 3)
  • Configurable timeout (default: 30s)
  • Abort support for cancellation

Circuit Breaker:

  • Tracks failures per endpoint
  • Opens after threshold (default: 5 failures)
  • Half-open state for recovery testing
  • Automatic reset after timeout

3. Connection Monitor (ApiConnectionMonitor.ts)

Location: lib/api/base/ApiConnectionMonitor.ts

Features:

  • Real-time connection status tracking
  • Health check polling (every 30s)
  • Reliability percentage calculation
  • Event emitter for status changes
  • Request/response time tracking

Statuses:

  • connected - Healthy API
  • degraded - Reduced reliability
  • disconnected - Unavailable
  • checking - Health check in progress

4. Enhanced BaseApiClient (BaseApiClient.ts)

Location: lib/api/base/BaseApiClient.ts

Enhancements:

  • Automatic error classification
  • Retry logic integration
  • Circuit breaker usage
  • Connection monitoring
  • Timeout handling
  • Request cancellation

Usage:

const client = new BaseApiClient(
  baseUrl,
  errorReporter,
  logger,
  { timeout: 30000, retry: true }
);

// All methods now support retry and graceful degradation
const data = await client.get('/api/endpoint');

5. Error UI Components

Location: components/errors/

Components:

  • ErrorDisplay.tsx - User-friendly error screen
  • DevErrorPanel.tsx - Developer debugging panel
  • ApiErrorBoundary.tsx - React error boundary
  • ApiStatusToolbar.tsx - Real-time status toolbar
  • NotificationIntegration.tsx - Notification system integration

6. Graceful Degradation (GracefulDegradation.ts)

Location: lib/api/base/GracefulDegradation.ts

Features:

  • Response caching with TTL
  • Fallback data support
  • Offline mode detection
  • Service wrapper for easy usage

Usage:

import { withGracefulDegradation, GracefulService } from './GracefulDegradation';

// Direct usage
const data = await withGracefulDegradation(
  () => apiClient.getData(),
  {
    fallback: defaultData,
    timeout: 5000,
    useCache: true
  }
);

// Service wrapper
const userService = new GracefulService(
  'user-data',
  () => apiClient.getUser(),
  defaultUser
);

const user = await userService.get();

7. Enhanced Error Reporter (EnhancedErrorReporter.ts)

Location: lib/infrastructure/EnhancedErrorReporter.ts

Features:

  • Environment-specific handling
  • User notification integration
  • Error buffering for batch reporting
  • Custom error handlers
  • External service integration (placeholder)

Integration in Application

Layout Integration

// apps/website/app/layout.tsx

import { NotificationIntegration } from '@/components/errors/NotificationIntegration';
import { ApiErrorBoundary } from '@/components/errors/ApiErrorBoundary';
import { ApiStatusToolbar } from '@/components/errors/ApiStatusToolbar';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <NotificationProvider>
          <NotificationIntegration />
          <ApiErrorBoundary>
            {/* Your app content */}
            {children}
            
            {/* Development toolbar */}
            {process.env.NODE_ENV === 'development' && (
              <ApiStatusToolbar position="bottom-right" autoHide={true} />
            )}
          </ApiErrorBoundary>
        </NotificationProvider>
      </body>
    </html>
  );
}

Service Integration

// lib/services/DriverService.ts

import { BaseApiClient } from '@/lib/api/base/BaseApiClient';
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/ConsoleLogger';

// Enhanced service with error handling
export class DriverService {
  private apiClient: BaseApiClient;
  private errorReporter: EnhancedErrorReporter;

  constructor() {
    const logger = new ConsoleLogger();
    this.errorReporter = new EnhancedErrorReporter(logger, {
      showUserNotifications: true,
      logToConsole: true,
      reportToExternal: process.env.NODE_ENV === 'production',
    });

    this.apiClient = new BaseApiClient(
      getWebsiteApiBaseUrl(),
      this.errorReporter,
      logger,
      { timeout: 30000, retry: true }
    );
  }

  async getDriverProfile(driverId: string): Promise<DriverProfileViewModel | null> {
    try {
      const dto = await this.apiClient.getDriverProfile(driverId);
      return new DriverProfileViewModel(dto);
    } catch (error) {
      // Error already handled by BaseApiClient
      // Return null for graceful degradation
      return null;
    }
  }
}

Development Tools

DevToolbar Integration

The enhanced DevToolbar includes:

  • Real-time API status indicator
  • Reliability percentage
  • Circuit breaker status
  • Health check button
  • Stats reset
  • Test error trigger

ApiStatusToolbar

A floating toolbar that shows:

  • Current connection status
  • Request statistics
  • Circuit breaker states
  • Quick actions for testing

Error Handling Patterns

1. Automatic Retry

// Automatically retries failed requests
const data = await client.get('/api/data');

2. Graceful Fallback

const data = await withGracefulDegradation(
  () => client.get('/api/data'),
  { fallback: defaultData }
);

3. Error Boundary

<ApiErrorBoundary>
  <MyComponent />
</ApiErrorBoundary>

4. Manual Error Handling

try {
  const data = await client.get('/api/data');
} catch (error) {
  if (isApiError(error)) {
    if (error.isRetryable()) {
      // Show retry button
    }
    if (error.isConnectivityIssue()) {
      // Show connection error
    }
  }
}

Environment-Specific Behavior

Development

  • Detailed error messages with stack traces
  • Developer error panel with full context
  • API status toolbar always visible
  • Console logging with full details
  • Test error triggers

Production

  • User-friendly error messages
  • Modal notifications for critical errors
  • Toast notifications for warnings
  • No sensitive data in error messages
  • Automatic error reporting to external services

Monitoring & Debugging

Connection Health

import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';

// Get current status
const status = connectionMonitor.getStatus();

// Get detailed health
const health = connectionMonitor.getHealth();

// Run manual health check
const result = await connectionMonitor.performHealthCheck();

Circuit Breaker Status

import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler';

const registry = CircuitBreakerRegistry.getInstance();
const status = registry.getStatus();

Error Buffer

import { getGlobalErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';

const reporter = getGlobalErrorReporter();
const errors = reporter.getBufferedErrors();
reporter.flush(); // Send batch

Best Practices

  1. Always use BaseApiClient for API calls
  2. Wrap critical components with ApiErrorBoundary
  3. Provide fallbacks for graceful degradation
  4. Use error classification for specific handling
  5. Monitor connection status for user feedback
  6. Test error scenarios in development
  7. Log appropriately based on severity
  8. Use caching for frequently accessed data

Troubleshooting

API Always Shows "Disconnected"

  • Check API_BASE_URL environment variable
  • Verify CORS configuration on API server
  • Check browser console for network errors
  • Use DevToolbar health check

Requests Not Retrying

  • Verify retry option is enabled in BaseApiClient
  • Check error type is retryable
  • Review circuit breaker status

Notifications Not Showing

  • Ensure NotificationProvider wraps your app
  • Check NotificationIntegration is mounted
  • Verify error severity levels

Circuit Breaker Stays Open

  • Check API server health
  • Reset circuit breakers via DevToolbar
  • Review error logs for root cause

Future Enhancements

  • External error reporting (Sentry, LogRocket)
  • Persistent cache storage (IndexedDB)
  • Offline queue for failed requests
  • Performance metrics dashboard
  • A/B testing for error recovery strategies