Claude Code Plugins

Community-maintained marketplace

Feedback
0
0

Comprehensive error handling patterns for API responses, React Error Boundaries, webhook processing, user-facing messages, and monitoring with Baselime.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name error-handling
description Comprehensive error handling patterns for API responses, React Error Boundaries, webhook processing, user-facing messages, and monitoring with Baselime.

Error Handling

Key Files

  • Error utilities: shared/utils/errors.ts
  • Global error boundary: app/error.tsx
  • Baselime monitoring: client/components/monitoring/BaselimeProvider.tsx
  • API middleware patterns: app/api/*/route.ts

Standard Error Response Format

All API errors must follow the standardized format:

interface IErrorResponse {
  success: false;
  error: {
    code: ErrorCode | string;
    message: string;
    details?: Record<string, unknown>;
    requestId?: string;
  };
}

// Usage
import { createErrorResponse, ErrorCodes } from '@shared/utils/errors';

return createErrorResponse(
  ErrorCodes.INSUFFICIENT_CREDITS,
  'You need 5 credits to upscale this image',
  402,
  { required: 5, current: 2 }
);

Common Error Codes

Use predefined error codes from ErrorCodes:

// 4xx Client Errors
ErrorCodes.INVALID_REQUEST - Malformed request
ErrorCodes.UNAUTHORIZED - Authentication required
ErrorCodes.FORBIDDEN - Permission denied
ErrorCodes.NOT_FOUND - Resource not found
ErrorCodes.INSUFFICIENT_CREDITS - Not enough credits
ErrorCodes.RATE_LIMITED - Too many requests
ErrorCodes.VALIDATION_ERROR - Invalid data
ErrorCodes.TIER_RESTRICTED - Feature requires higher tier

// 5xx Server Errors
ErrorCodes.INTERNAL_ERROR - Unexpected server error
ErrorCodes.AI_UNAVAILABLE - AI service down
ErrorCodes.PROCESSING_FAILED - Processing operation failed

React Error Boundaries

Global Error Boundary

The global error boundary (app/error.tsx) catches all unhandled errors:

'use client';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // Log to monitoring
    console.error('Global error caught:', error);

    // Send to Baselime if available
    if (window.baselime) {
      window.baselime.logError(error, {
        digest: error.digest,
        boundary: 'global-error',
      });
    }
  }, [error]);

  return (
    <div>
      <h1>Application Error</h1>
      <p>We encountered an unexpected error.</p>
      <button onClick={reset}>Try Again</button>
    </div>
  );
}

Component-Level Error Boundaries

Create reusable error boundaries for specific features:

'use client';

interface IErrorBoundaryProps {
  children: ReactNode;
  fallback?: ComponentType<{ error: Error; reset: () => void }>;
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
}

export function ErrorBoundary({
  children,
  fallback: FallbackComponent,
  onError
}: IErrorBoundaryProps) {
  return (
    <div className="error-boundary">
      {children}
    </div>
  );
}

API Route Error Handling

Route Handler Pattern

import { createErrorResponse, ErrorCodes } from '@shared/utils/errors';
import { NextRequest } from 'next/server';

export async function POST(request: NextRequest) {
  try {
    // Validate request
    const body = await request.json();
    if (!body.required) {
      return createErrorResponse(ErrorCodes.VALIDATION_ERROR, 'Missing required field: required');
    }

    // Process request
    const result = await processData(body);

    return Response.json({ success: true, data: result });
  } catch (error) {
    console.error('Route handler error:', error);

    // Handle specific errors
    if (error instanceof AppError) {
      return createErrorResponse(error.code, error.message, error.statusCode, error.details);
    }

    // Handle unexpected errors
    return createErrorResponse(ErrorCodes.INTERNAL_ERROR, 'An unexpected error occurred', 500);
  }
}

Validation Error Pattern

import { z } from 'zod';
import { createErrorResponse, ErrorCodes } from '@shared/utils/errors';

const schema = z.object({
  email: z.string().email(),
  credits: z.number().min(0),
});

export async function validateInput(data: unknown) {
  try {
    return schema.parse(data);
  } catch (error) {
    if (error instanceof z.ZodError) {
      return createErrorResponse(ErrorCodes.VALIDATION_ERROR, 'Invalid request data', 400, {
        fields: error.errors.map(e => e.path.join('.')),
      });
    }
    throw error;
  }
}

Webhook Error Handling

Stripe Webhooks

import Stripe from 'stripe';
import { createErrorResponse, ErrorCodes } from '@shared/utils/errors';

export async function handleWebhook(request: Request) {
  const body = await request.text();
  const signature = request.headers.get('stripe-signature');

  try {
    // Verify webhook signature
    const event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET
    );

    // Process event
    await processWebhookEvent(event);

    return Response.json({ received: true });
  } catch (error) {
    // Retry on signature verification failure
    if (error instanceof Stripe.errors.StripeSignatureError) {
      console.error('Webhook signature verification failed:', error);
      return new Response('Webhook signature verification failed', {
        status: 401,
      });
    }

    // Log other errors but respond with success to avoid retries
    console.error('Webhook processing error:', error);
    return Response.json({ received: true });
  }
}

Retry Pattern for Webhooks

export async function processWithRetry<T>(
  operation: () => Promise<T>,
  maxRetries: number = 3,
  delay: number = 1000
): Promise<T> {
  let lastError: Error;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error as Error;

      if (attempt === maxRetries) {
        break;
      }

      // Exponential backoff
      await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, attempt)));
    }
  }

  throw lastError!;
}

User-Facing Error Messages

Client-Side Error Display

'use client';

import { useNotificationStore } from '@client/store/notifications';

interface IApiError {
  code: string;
  message: string;
  details?: Record<string, unknown>;
}

export function useErrorHandler() {
  const { addNotification } = useNotificationStore();

  return (error: IApiError | Error | unknown) => {
    if (error && typeof error === 'object' && 'code' in error) {
      const apiError = error as IApiError;

      // Map error codes to user-friendly messages
      const userMessages: Record<string, string> = {
        [ErrorCodes.INSUFFICIENT_CREDITS]:
          'You need more credits to perform this action. Please upgrade your plan.',
        [ErrorCodes.RATE_LIMITED]: 'Too many requests. Please wait a moment and try again.',
        [ErrorCodes.TIER_RESTRICTED]: 'This feature is available with a paid subscription.',
        [ErrorCodes.AI_UNAVAILABLE]:
          'Our AI service is temporarily unavailable. Please try again later.',
      };

      const message = userMessages[apiError.code] || apiError.message;

      addNotification({
        type: 'error',
        title: 'Error',
        message,
        duration: 5000,
      });
    } else {
      // Handle generic errors
      addNotification({
        type: 'error',
        title: 'Error',
        title: 'An unexpected error occurred',
        message: 'Please try again or contact support if the issue persists.',
        duration: 5000,
      });
    }
  };
}

Graceful Degradation

'use client';

interface IDataLoaderProps {
  url: string;
  fallback?: ReactNode;
  children: (data: unknown) => ReactNode;
}

export function DataLoader({ url, fallback, children }: IDataLoaderProps) {
  const { data, error, isLoading } = useFetch(url);

  if (isLoading) {
    return <LoadingSpinner />;
  }

  if (error) {
    // Show fallback if provided
    if (fallback) {
      return fallback;
    }

    // Show error message with retry
    return (
      <div className="error-state">
        <p>Failed to load data</p>
        <button onClick={() => window.location.reload()}>
          Try Again
        </button>
      </div>
    );
  }

  return <>{children(data)}</>;
}

Error Monitoring with Baselime

Setup Provider

// app/providers.tsx
import { BaselimeProvider } from '@client/components/monitoring/BaselimeProvider';

export function Providers({ children }: { children: ReactNode }) {
  return (
    <BaselimeProvider>
      <QueryClientProvider>{children}</QueryClientProvider>
    </BaselimeProvider>
  );
}

Error Logging

// Custom error logging hook
export function useErrorLogging() {
  return (error: Error, context?: Record<string, unknown>) => {
    // Always log to console
    console.error('Application error:', error, context);

    // Send to Baselime if available
    if (typeof window !== 'undefined' && window.baselime) {
      window.baselime.logError(error, {
        url: window.location.href,
        userAgent: navigator.userAgent,
        timestamp: new Date().toISOString(),
        ...context,
      });
    }
  };
}

Server-Side Error Logging

// server/utils/logging.ts
import baselime from '@baselime/node-logger';

export function logError(error: Error, context?: Record<string, unknown>) {
  const logData = {
    error: {
      name: error.name,
      message: error.message,
      stack: error.stack,
    },
    context,
    timestamp: new Date().toISOString(),
  };

  console.error('Server error:', logData);

  // Send to Baselime
  baselime.error('Server error', logData);
}

Error Handling Best Practices

1. Validate Early, Fail Fast

// Good: Validate at the edge
export async function handleUpload(request: Request) {
  const { file, options } = await validateUploadRequest(request);
  // Proceed with valid data
}

// Bad: Validate deep in the logic
export async function processUpload(data: unknown) {
  // Validate here after expensive operations
}

2. Use Specific Error Types

// Create custom error classes
export class InsufficientCreditsError extends AppError {
  constructor(required: number, current: number) {
    super(ErrorCodes.INSUFFICIENT_CREDITS, `Need ${required} credits, have ${current}`, 402, {
      required,
      current,
    });
  }
}

3. Never Expose Stack Traces to Users

// Good: Safe error responses
return createErrorResponse(ErrorCodes.INTERNAL_ERROR, 'An unexpected error occurred');

// Bad: Exposing internals
return Response.json({
  error: error.stack, // Security risk!
});

4. Implement Circuit Breakers

class CircuitBreaker {
  private failures = 0;
  private lastFailure = 0;
  private state: 'closed' | 'open' | 'half-open' = 'closed';

  async execute<T>(operation: () => Promise<T>): Promise<T> {
    if (this.state === 'open') {
      throw new Error('Circuit breaker is open');
    }

    try {
      const result = await operation();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
}

5. Request ID Tracking

// middleware.ts
export function middleware(request: NextRequest) {
  const requestId = crypto.randomUUID();
  request.headers.set('x-request-id', requestId);
  // Continue...
}

// API route
export async function GET(request: NextRequest) {
  const requestId = request.headers.get('x-request-id');

  try {
    // Process request...
  } catch (error) {
    return createErrorResponse(
      ErrorCodes.INTERNAL_ERROR,
      'An error occurred',
      500,
      undefined,
      requestId
    );
  }
}

Testing Error Scenarios

// Test error responses
describe('API Error Handling', () => {
  it('returns proper error format for validation errors', async () => {
    const response = await POST(
      new Request('http://localhost/api/test', {
        method: 'POST',
        body: JSON.stringify({ invalid: 'data' }),
      })
    );

    const body = await response.json();

    expect(response.status).toBe(400);
    expect(body.success).toBe(false);
    expect(body.error.code).toBe('VALIDATION_ERROR');
    expect(body.error.message).toBeDefined();
  });
});

Environment-Specific Handling

// Development: Show detailed errors
if (isDevelopment()) {
  return createErrorResponse(ErrorCodes.INTERNAL_ERROR, error.message, 500, { stack: error.stack });
}

// Production: Generic error message
return createErrorResponse(ErrorCodes.INTERNAL_ERROR, 'An unexpected error occurred');