api-error-handling

CoderMariusz's avatarfrom CoderMariusz

Apply when designing error responses, implementing error handlers, and ensuring consistent error format across APIs.

⭐1stars🔀0forks📁View on GitHub🕐Updated Jan 4, 2026

When & Why to Use This Skill

This Claude skill provides a comprehensive framework for designing and implementing standardized API error responses. It leverages industry best practices and standards like RFC 9457 (Problem Details) to ensure consistent error formats across backend services. By providing structured patterns for error class hierarchies, global middleware handlers, and frontend consumption, it helps developers improve API reliability, security, and the overall developer experience (DX).

Use Cases

  • Designing a standardized error response structure for a new RESTful API to ensure consistency across multiple microservices.
  • Implementing global error handling middleware in Express.js or Next.js to catch exceptions and return machine-readable responses without leaking sensitive stack traces.
  • Creating a reusable TypeScript error class hierarchy to manage specific application failures such as validation errors, 404 Not Found, and unauthorized access.
  • Developing frontend API clients that can gracefully parse structured error codes to provide actionable feedback to end-users.
  • Refactoring legacy backend code to replace inconsistent '200 OK' error responses with appropriate HTTP status codes and standardized error objects.
nameapi-error-handling
descriptionApply when designing error responses, implementing error handlers, and ensuring consistent error format across APIs.
version1.1.0
tokens~650
confidencehigh
- https//datatracker.ietf.org/doc/html/rfc9110#section-6
last_validated2025-12-10
next_review2025-12-24
tags[api, error-handling, backend, rest]

When to Use

Apply when designing error responses, implementing error handlers, and ensuring consistent error format across APIs.

Patterns

Pattern 1: Standard Error Response Format

// Source: https://www.rfc-editor.org/rfc/rfc9457 (Problem Details)
interface ApiError {
  error: {
    code: string;           // Machine-readable code
    message: string;        // Human-readable message
    details?: ErrorDetail[];// Field-level errors
    requestId?: string;     // For debugging
  };
}

interface ErrorDetail {
  field: string;
  message: string;
  code?: string;
}

// Example response
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      { "field": "email", "message": "Invalid email format", "code": "INVALID_FORMAT" },
      { "field": "age", "message": "Must be positive", "code": "INVALID_RANGE" }
    ],
    "requestId": "req_abc123"
  }
}

Pattern 2: Error Class Hierarchy

// Source: Best practice pattern
class AppError extends Error {
  constructor(
    public code: string,
    message: string,
    public statusCode: number,
    public details?: ErrorDetail[]
  ) {
    super(message);
    this.name = 'AppError';
  }
}

class ValidationError extends AppError {
  constructor(details: ErrorDetail[]) {
    super('VALIDATION_ERROR', 'Validation failed', 400, details);
  }
}

class NotFoundError extends AppError {
  constructor(resource: string) {
    super('NOT_FOUND', `${resource} not found`, 404);
  }
}

class UnauthorizedError extends AppError {
  constructor() {
    super('UNAUTHORIZED', 'Authentication required', 401);
  }
}

Pattern 3: Global Error Handler (Express/Next.js)

// Source: Best practice pattern
function errorHandler(
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
) {
  // Log for debugging
  console.error('Error:', {
    message: err.message,
    stack: err.stack,
    requestId: req.headers['x-request-id'],
  });

  if (err instanceof AppError) {
    return res.status(err.statusCode).json({
      error: {
        code: err.code,
        message: err.message,
        details: err.details,
        requestId: req.headers['x-request-id'],
      },
    });
  }

  // Unknown error - don't leak details
  return res.status(500).json({
    error: {
      code: 'INTERNAL_ERROR',
      message: 'An unexpected error occurred',
      requestId: req.headers['x-request-id'],
    },
  });
}

Pattern 4: Frontend Error Handling

// Source: Best practice pattern
async function apiCall<T>(url: string, options?: RequestInit): Promise<T> {
  const response = await fetch(url, options);

  if (!response.ok) {
    const error = await response.json();
    throw new ApiError(error.error.code, error.error.message, error.error.details);
  }

  return response.json();
}

// Usage with error handling
try {
  const user = await apiCall<User>('/api/users/123');
} catch (error) {
  if (error instanceof ApiError) {
    if (error.code === 'NOT_FOUND') {
      showNotification('User not found');
    } else if (error.code === 'VALIDATION_ERROR') {
      setFormErrors(error.details);
    }
  }
}

Pattern 5: Error Code Constants

// Source: Best practice pattern
export const ErrorCodes = {
  VALIDATION_ERROR: 'VALIDATION_ERROR',
  NOT_FOUND: 'NOT_FOUND',
  UNAUTHORIZED: 'UNAUTHORIZED',
  FORBIDDEN: 'FORBIDDEN',
  CONFLICT: 'CONFLICT',
  RATE_LIMITED: 'RATE_LIMITED',
  INTERNAL_ERROR: 'INTERNAL_ERROR',
} as const;

type ErrorCode = typeof ErrorCodes[keyof typeof ErrorCodes];

Anti-Patterns

  • Exposing stack traces - Never in production
  • Generic "Error occurred" - Provide actionable messages
  • 200 for errors - Use appropriate HTTP status codes
  • Inconsistent format - Same structure for all errors

Verification Checklist

  • All errors have code + message
  • Status codes match error type
  • Validation errors include field details
  • Stack traces hidden in production
  • Request ID for debugging correlation