Files
vip-coordinator/ERROR_HANDLING.md
kyle 868f7efc23
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
Major Enhancement: NestJS Migration + CASL Authorization + Error Handling
Complete rewrite from Express to NestJS with enterprise-grade features:

## Backend Improvements
- Migrated from Express to NestJS 11.0.1 with TypeScript
- Implemented Prisma ORM 7.3.0 for type-safe database access
- Added CASL authorization system replacing role-based guards
- Created global exception filters with structured logging
- Implemented Auth0 JWT authentication with Passport.js
- Added vehicle management with conflict detection
- Enhanced event scheduling with driver/vehicle assignment
- Comprehensive error handling and logging

## Frontend Improvements
- Upgraded to React 19.2.0 with Vite 7.2.4
- Implemented CASL-based permission system
- Added AbilityContext for declarative permissions
- Created ErrorHandler utility for consistent error messages
- Enhanced API client with request/response logging
- Added War Room (Command Center) dashboard
- Created VIP Schedule view with complete itineraries
- Implemented Vehicle Management UI
- Added mock data generators for testing (288 events across 20 VIPs)

## New Features
- Vehicle fleet management (types, capacity, status tracking)
- Complete 3-day Jamboree schedule generation
- Individual VIP schedule pages with PDF export (planned)
- Real-time War Room dashboard with auto-refresh
- Permission-based navigation filtering
- First user auto-approval as administrator

## Documentation
- Created CASL_AUTHORIZATION.md (comprehensive guide)
- Created ERROR_HANDLING.md (error handling patterns)
- Updated CLAUDE.md with new architecture
- Added migration guides and best practices

## Technical Debt Resolved
- Removed custom authentication in favor of Auth0
- Replaced role checks with CASL abilities
- Standardized error responses across API
- Implemented proper TypeScript typing
- Added comprehensive logging

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-31 08:50:25 +01:00

8.9 KiB

Error Handling Guide

This document describes the error handling patterns implemented in the VIP Coordinator application.

Backend Error Handling

Global Exception Filters

The backend uses two global exception filters for consistent error responses:

1. HttpExceptionFilter

Handles all HTTP exceptions (4xx, 5xx errors) and formats them consistently.

Features:

  • Standardized error response format
  • Automatic logging with appropriate levels (error/warn/log)
  • Request context logging (params, query, body)
  • Sensitive data redaction (passwords, tokens, API keys)

Error Response Format:

{
  "statusCode": 400,
  "timestamp": "2026-01-25T10:30:00.000Z",
  "path": "/api/v1/events",
  "method": "POST",
  "message": "Validation failed",
  "error": "Bad Request",
  "details": { ... }
}

2. AllExceptionsFilter

Catch-all filter for unhandled errors (database errors, runtime exceptions, etc.).

Features:

  • Catches all non-HTTP exceptions
  • Prevents server crashes from unhandled errors
  • Logs full stack traces
  • Returns 500 Internal Server Error to client
  • In development: includes stack trace in response

Using Exceptions in Controllers/Services

Example:

import { BadRequestException, NotFoundException } from '@nestjs/common';

// Simple error
throw new NotFoundException('VIP not found');

// Error with details
throw new BadRequestException({
  message: 'Driver has conflicting events',
  conflicts: [
    { id: '123', title: 'Airport Pickup', startTime: '...' }
  ]
});

Logging Best Practices

import { Logger } from '@nestjs/common';

export class MyService {
  private readonly logger = new Logger(MyService.name);

  async doSomething() {
    this.logger.log('Starting operation...');
    this.logger.warn('Conflict detected');
    this.logger.error('Operation failed', error.stack);
  }
}

Frontend Error Handling

ErrorHandler Utility

The ErrorHandler class provides centralized error handling for the frontend.

Location: frontend/src/lib/errorHandler.ts

Quick Usage

import { handleError } from '@/lib/errorHandler';

try {
  await api.createVIP(data);
  toast.success('VIP created successfully');
} catch (error) {
  handleError('VIP Creation', error, 'Failed to create VIP');
}

Advanced Usage

Extract Error Message Only

import { ErrorHandler } from '@/lib/errorHandler';

try {
  await api.get('/vips');
} catch (error) {
  const message = ErrorHandler.getMessage(error, 'Failed to load VIPs');
  setErrorMessage(message);
}

Show Toast Only

ErrorHandler.showError(error, 'Operation failed');

Log Only (No Toast)

ErrorHandler.log('VIP Fetch', error, { vipId: '123' });

Check Error Type

try {
  await api.updateVIP(id, data);
} catch (error) {
  if (ErrorHandler.isAuthError(error)) {
    // Redirect to login
    navigate('/login');
  } else if (ErrorHandler.isConflict(error)) {
    // Show conflict resolution UI
    const conflicts = ErrorHandler.getConflicts(error);
    setConflicts(conflicts);
  } else {
    ErrorHandler.showError(error);
  }
}

Available Methods

Method Description Example
getMessage(error, fallback?) Extract user-friendly message const msg = ErrorHandler.getMessage(error)
showError(error, fallback?) Show error toast ErrorHandler.showError(error)
log(context, error, info?) Log to console with context ErrorHandler.log('API', error, { id })
handle(context, error, fallback?, info?) Log + toast ErrorHandler.handle('Save', error)
isAuthError(error) Check if 401/403 if (ErrorHandler.isAuthError(error))
isConflict(error) Check if 409 if (ErrorHandler.isConflict(error))
isValidationError(error) Check if 400 if (ErrorHandler.isValidationError(error))
getConflicts(error) Extract conflicts array const conflicts = ErrorHandler.getConflicts(error)

API Client Logging

The axios client automatically logs all requests and responses in development mode.

Console output format:

[API] → POST /api/v1/vips { data: {...} }
[API] ← 201 POST /api/v1/vips { data: {...} }
[API] ✖ 400 POST /api/v1/events { status: 400, data: {...} }

Error types logged:

  • 401: Authentication required
  • 403: Permission denied
  • 404: Resource not found
  • 409: Conflict (with conflict details)
  • 500+: Server error

Migration Guide

Before (Old Pattern)

try {
  await api.createVIP(data);
  toast.success('VIP created');
} catch (error: any) {
  console.error('Failed to create VIP:', error);
  toast.error(error.response?.data?.message || 'Failed to create VIP');
}

After (New Pattern)

import { handleError } from '@/lib/errorHandler';

try {
  await api.createVIP(data);
  toast.success('VIP created');
} catch (error) {
  handleError('VIP Creation', error, 'Failed to create VIP');
}

Benefits:

  • Consistent error messages across app
  • Automatic console logging with context
  • Handles all error types (Axios, Error, unknown)
  • Type-safe error checking
  • Less code duplication

Common Error Scenarios

1. Network Error (Backend Down)

Backend logs: None (backend not reachable) Frontend logs: [API] ✖ Network error - no response received User sees: Toast: "Network error. Please check your connection."

2. Authentication Error (401)

Backend logs: [WARN] [GET] /api/v1/vips - 401 - Unauthorized Frontend logs: [API] ✖ 401 GET /api/v1/vips + warning User sees: Toast: "Authentication required. Please log in again."

3. Permission Error (403)

Backend logs: [WARN] [POST] /api/v1/users - 403 - Forbidden resource Frontend logs: [API] ✖ 403 POST /api/v1/users + warning User sees: Toast: "You do not have permission to perform this action."

4. Validation Error (400)

Backend logs: [WARN] [POST] /api/v1/events - 400 - Validation failed Frontend logs: [API] ✖ 400 POST /api/v1/events + response data User sees: Toast: "Validation failed: email must be a valid email"

5. Conflict Error (409)

Backend logs: [WARN] [POST] /api/v1/events - 409 - Driver has conflicting events Frontend logs: [API] ✖ 409 POST /api/v1/events + conflicts array User sees: Toast: "A conflict occurred. Please check for overlapping schedules."

6. Server Error (500)

Backend logs: [ERROR] [GET] /api/v1/vips - 500 - Internal server error + stack trace Frontend logs: [API] ✖ 500 GET /api/v1/vips + error message User sees: Toast: "Server error. Please try again later."

Best Practices

DO

// Use handleError for simple cases
try {
  await api.post('/vips', data);
  toast.success('Success');
} catch (error) {
  handleError('VIP Creation', error);
}

// Use ErrorHandler methods for complex cases
try {
  await api.updateEvent(id, data);
  toast.success('Event updated');
} catch (error) {
  if (ErrorHandler.isConflict(error)) {
    // Show conflict resolution dialog
    showConflictDialog(ErrorHandler.getConflicts(error));
  } else {
    ErrorHandler.showError(error);
  }
}

// Log errors with context
ErrorHandler.log('Event Update', error, { eventId: id, changes: data });

// Use structured logging in backend
this.logger.log(`Created VIP: ${vip.name} (ID: ${vip.id})`);
this.logger.error(`Failed to create VIP: ${error.message}`, error.stack);

DON'T

// Don't use generic error messages
catch (error) {
  toast.error('An error occurred'); // Too vague!
}

// Don't ignore errors
catch (error) {
  console.log(error); // Use console.error and proper context
}

// Don't manually extract error messages
catch (error: any) {
  const msg = error.response?.data?.message || 'Failed'; // Use ErrorHandler.getMessage()
  toast.error(msg);
}

// Don't log sensitive data
this.logger.log(`User login: ${email} with password ${password}`); // Never log passwords!

Debugging Tips

Enable Verbose Logging

Backend: Set LOG_LEVEL=debug in .env

Frontend: Development mode already has verbose logging enabled

Check Error Context

Backend logs include:

  • Timestamp
  • HTTP method
  • URL path
  • Status code
  • Request params/query/body
  • User email (if authenticated)

Frontend logs include:

  • Timestamp
  • Context (feature area)
  • HTTP method
  • URL
  • Request/response data
  • Error type

Common Issues

"Network error" but backend is running:

  • Check CORS configuration
  • Verify API_URL in .env
  • Check browser DevTools Network tab

"Authentication required" after login:

  • Check if token is stored in localStorage
  • Verify Auth0 configuration
  • Check if user is approved in database

Validation errors don't show field names:

  • Backend DTO needs proper validation decorators
  • Use class-validator's @IsString(), @IsEmail(), etc.

Last Updated: 2026-01-25 See also: CLAUDE.md for general project documentation