Minimal H(TTP) framework built for high performance and portability across multiple JavaScript runtimes.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
HTTP error handling with status codes, custom error data, structured error responses, and comprehensive error management utilities.
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" }
});
}
});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 };
});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 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;
}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
}
});
}
}
});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 };
});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
};
});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 };
});