CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-h3

Minimal H(TTP) framework built for high performance and portability across multiple JavaScript runtimes.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

HTTP error handling with status codes, custom error data, structured error responses, and comprehensive error management utilities.

Capabilities

HTTPError Class

The main error class for HTTP-specific errors with status codes and metadata.

/**
 * HTTP error class with status code and additional metadata
 */
class HTTPError extends Error {
  /**
   * HTTP status code
   */
  status: number;
  
  /**
   * HTTP status text
   */
  statusText: string | undefined;
  
  /**
   * Additional HTTP headers to send with error
   */
  headers: Headers | undefined;
  
  /**
   * Original error that caused this HTTPError
   */
  cause: unknown | undefined;
  
  /**
   * Custom data associated with the error
   */
  data: any | undefined;
  
  /**
   * JSON body properties for error response
   */
  body: Record<string, unknown> | undefined;
  
  /**
   * Flag indicating if error was handled
   */
  unhandled: boolean | undefined;
  
  /**
   * Create HTTPError with message and optional details
   * @param message - Error message
   * @param details - Optional error details
   */
  constructor(message: string, details?: ErrorDetails);
  
  /**
   * Create HTTPError with error details object
   * @param details - Error details including status, message, etc.
   */
  constructor(details: ErrorDetails);
  
  /**
   * Serialize error to JSON for response
   * @returns JSON representation of error
   */
  toJSON(): ErrorBody;
  
  /**
   * Check if input is an HTTPError instance
   * @param input - Value to check
   * @returns True if input is HTTPError
   */
  static isError(input: any): input is HTTPError;
  
  /**
   * Create HTTPError with specific status code
   * @param status - HTTP status code
   * @param statusText - Optional status text
   * @param details - Optional additional details
   * @returns HTTPError instance
   */
  static status(status: number, statusText?: string, details?: ErrorDetails): HTTPError;
}

Usage Examples:

import { HTTPError } from "h3";

// Basic error creation
const basicHandler = defineHandler((event) => {
  const { id } = getRouterParams(event);
  
  if (!id) {
    throw new HTTPError("ID parameter is required", { status: 400 });
  }
  
  const user = findUser(id);
  if (!user) {
    throw HTTPError.status(404, "Not Found", {
      data: { resource: "user", id }
    });
  }
  
  return user;
});

// Error with custom data
const validationHandler = defineHandler(async (event) => {
  const body = await readBody(event);
  
  const errors = validateUser(body);
  if (errors.length > 0) {
    throw new HTTPError({
      status: 422,
      statusText: "Validation Error",
      message: "Invalid user data",
      data: { errors, input: body }
    });
  }
  
  return createUser(body);
});

// Error with headers
const authHandler = defineHandler((event) => {
  const token = getHeader(event, "authorization");
  
  if (!token) {
    throw new HTTPError("Authentication required", {
      status: 401,
      headers: new Headers({
        "WWW-Authenticate": "Bearer realm=\"api\""
      })
    });
  }
  
  return { authenticated: true };
});

// Error with cause
const externalHandler = defineHandler(async (event) => {
  try {
    const data = await fetchExternalAPI();
    return data;
  } catch (originalError) {
    throw new HTTPError("External service unavailable", {
      status: 503,
      cause: originalError,
      data: { service: "external-api" }
    });
  }
});

Status Code Helpers

Quick helpers for common HTTP status codes.

// Common status code patterns
const statusHelpers = {
  badRequest: (message?: string, data?: any) => 
    HTTPError.status(400, "Bad Request", { message, data }),
  
  unauthorized: (message?: string) => 
    HTTPError.status(401, "Unauthorized", { message }),
  
  forbidden: (message?: string) => 
    HTTPError.status(403, "Forbidden", { message }),
  
  notFound: (resource?: string, id?: string) => 
    HTTPError.status(404, "Not Found", { 
      message: resource ? `${resource} not found` : "Resource not found",
      data: { resource, id }
    }),
  
  methodNotAllowed: (allowed: string[]) => 
    HTTPError.status(405, "Method Not Allowed", {
      headers: new Headers({ "Allow": allowed.join(", ") })
    }),
  
  conflict: (message?: string, data?: any) => 
    HTTPError.status(409, "Conflict", { message, data }),
  
  unprocessable: (errors: any[]) => 
    HTTPError.status(422, "Unprocessable Entity", { 
      message: "Validation failed",
      data: { errors }
    }),
  
  tooManyRequests: (retryAfter?: number) => 
    HTTPError.status(429, "Too Many Requests", {
      headers: retryAfter ? new Headers({ "Retry-After": retryAfter.toString() }) : undefined
    }),
  
  internalServerError: (message?: string) => 
    HTTPError.status(500, "Internal Server Error", { message }),
  
  notImplemented: (feature?: string) => 
    HTTPError.status(501, "Not Implemented", { 
      message: feature ? `${feature} not implemented` : "Not implemented"
    }),
  
  serviceUnavailable: (retryAfter?: number) => 
    HTTPError.status(503, "Service Unavailable", {
      headers: retryAfter ? new Headers({ "Retry-After": retryAfter.toString() }) : undefined
    })
};

Usage Examples:

// Use status helpers
const resourceHandler = defineHandler((event) => {
  const { id } = getRouterParams(event);
  
  if (!id) {
    throw statusHelpers.badRequest("ID parameter required");
  }
  
  const resource = findResource(id);
  if (!resource) {
    throw statusHelpers.notFound("resource", id);
  }
  
  return resource;
});

// Validation errors
const validationHandler = defineHandler(async (event) => {
  const body = await readBody(event);
  const errors = validate(body);
  
  if (errors.length > 0) {
    throw statusHelpers.unprocessable(errors);
  }
  
  return { valid: true };
});

// Rate limiting
const rateLimitHandler = defineHandler((event) => {
  const ip = getRequestIP(event);
  
  if (isRateLimited(ip)) {
    const resetTime = getRateLimitReset(ip);
    throw statusHelpers.tooManyRequests(resetTime);
  }
  
  return { allowed: true };
});

Error Response Handling

Handle and format error responses consistently.

/**
 * Global error handler pattern
 */
const errorHandler = onError((error, event) => {
  console.error(`Error in ${event.url}:`, error);
  
  // Handle HTTPError instances
  if (HTTPError.isError(error)) {
    const response = error.toJSON();
    
    // Add debug info in development
    if (process.env.NODE_ENV === "development") {
      response.stack = error.stack;
      response.cause = error.cause;
    }
    
    return response;
  }
  
  // Handle other errors
  return {
    message: "Internal Server Error",
    status: 500,
    ...(process.env.NODE_ENV === "development" && {
      stack: error.stack,
      name: error.name
    })
  };
});

Usage Examples:

// Apply global error handler
const app = new H3({
  onError: errorHandler
});

// Custom error formatting
const formatError = (error: HTTPError, event: H3Event) => {
  const baseResponse = error.toJSON();
  
  return {
    ...baseResponse,
    timestamp: new Date().toISOString(),
    path: event.url.pathname,
    method: event.req.method,
    requestId: event.context.requestId
  };
};

// Route-specific error handling
const protectedHandler = defineHandler({
  handler: async (event) => {
    // Protected logic
    return { data: "protected" };
  },
  onError: [
    (error, event) => {
      if (HTTPError.isError(error) && error.status === 401) {
        // Custom unauthorized handling
        return {
          error: "Authentication required",
          loginUrl: "/login",
          timestamp: Date.now()
        };
      }
    }
  ]
});

Error Types

Error Interface Definitions

/**
 * Error details for HTTPError construction
 */
interface ErrorDetails {
  /**
   * HTTP status code
   */
  status?: number;
  
  /**
   * HTTP status text
   */
  statusText?: string;
  
  /**
   * Additional headers to send
   */
  headers?: HeadersInit;
  
  /**
   * Original error cause
   */
  cause?: unknown;
  
  /**
   * Custom data for error
   */
  data?: any;
  
  /**
   * Custom error message
   */
  message?: string;
}

/**
 * Error input type (union of message and details)
 */
type ErrorInput<DataT = any> = string | (ErrorDetails & { data?: DataT });

/**
 * JSON error response body
 */
interface ErrorBody<DataT = any> {
  /**
   * Error message
   */
  message: string;
  
  /**
   * HTTP status code
   */
  status?: number;
  
  /**
   * HTTP status text
   */
  statusText?: string;
  
  /**
   * Custom error data
   */
  data?: DataT;
}

Advanced Error Handling

Error Recovery

Implement error recovery and fallback mechanisms.

// Error recovery pattern
const resilientHandler = defineHandler(async (event) => {
  try {
    // Primary operation
    return await primaryService.getData();
  } catch (error) {
    console.warn("Primary service failed, trying fallback:", error);
    
    try {
      // Fallback operation
      return await fallbackService.getData();
    } catch (fallbackError) {
      // Both failed, return error with context
      throw new HTTPError("Service unavailable", {
        status: 503,
        data: {
          primaryError: error.message,
          fallbackError: fallbackError.message
        }
      });
    }
  }
});

Error Aggregation

Collect and aggregate multiple errors.

// Error aggregation pattern
const batchHandler = defineHandler(async (event) => {
  const requests = await readBody(event);
  const results = [];
  const errors = [];
  
  for (const [index, request] of requests.entries()) {
    try {
      const result = await processRequest(request);
      results.push({ index, result });
    } catch (error) {
      errors.push({ 
        index, 
        error: HTTPError.isError(error) ? error.toJSON() : { message: error.message }
      });
    }
  }
  
  if (errors.length > 0) {
    throw new HTTPError("Batch processing failed", {
      status: 207, // Multi-Status
      data: { results, errors }
    });
  }
  
  return { results };
});

Structured Error Logging

Implement structured error logging for better debugging.

// Structured error logging
const structuredErrorHandler = onError((error, event) => {
  const errorLog = {
    timestamp: new Date().toISOString(),
    level: "error",
    message: error.message,
    status: HTTPError.isError(error) ? error.status : 500,
    method: event.req.method,
    url: event.url.toString(),
    userAgent: event.req.headers.get("user-agent"),
    ip: getRequestIP(event),
    requestId: event.context.requestId,
    stack: error.stack,
    ...(HTTPError.isError(error) && {
      data: error.data,
      cause: error.cause
    })
  };
  
  // Log to your preferred logging service
  logger.error(errorLog);
  
  // Return sanitized error for client
  if (HTTPError.isError(error)) {
    return error.toJSON();
  }
  
  return {
    message: "Internal Server Error",
    status: 500,
    requestId: event.context.requestId
  };
});

Error Monitoring

Integrate with error monitoring services.

// Error monitoring integration
const monitoringErrorHandler = onError(async (error, event) => {
  // Send to monitoring service
  await errorMonitoringService.captureException(error, {
    tags: {
      method: event.req.method,
      status: HTTPError.isError(error) ? error.status : 500
    },
    extra: {
      url: event.url.toString(),
      headers: Object.fromEntries(event.req.headers.entries()),
      context: event.context
    }
  });
  
  // Continue with normal error handling
  if (HTTPError.isError(error)) {
    return error.toJSON();
  }
  
  return { message: "Internal Server Error", status: 500 };
});

docs

advanced-features.md

core-framework.md

error-handling.md

event-handling.md

handlers-middleware.md

index.md

request-processing.md

response-handling.md

runtime-adapters.md

web-utilities.md

tile.json