UNPKG

@pulzar/core

Version:

Next-generation Node.js framework for ultra-fast web applications with zero-reflection DI, GraphQL, WebSockets, events, and edge runtime support

161 lines 5.35 kB
import { logger } from "../utils/logger"; export class FastifyErrorHandler { options; constructor(options = {}) { this.options = { includeStack: process.env.NODE_ENV === "development", formatError: this.defaultErrorFormatter, customErrorCodes: {}, logErrors: true, ...options, }; } defaultErrorFormatter = (error) => { return { error: error.name || "Error", message: error.message || "An error occurred", statusCode: error.statusCode || 500, timestamp: new Date().toISOString(), }; }; /** * Fastify error handler */ handler() { return async (error, request, reply) => { // Get request ID const requestId = request.id; // Log error if (this.options.logErrors) { logger.error("Request error", { error: error.message, stack: error.stack, statusCode: error.statusCode || 500, method: request.method, url: request.url, userAgent: request.headers["user-agent"], ip: request.ip, requestId, }); } // Format error response const errorResponse = this.options.formatError(error); errorResponse.requestId = requestId; // Add stack trace in development if (this.options.includeStack && error.stack) { errorResponse.stack = error.stack; } // Determine status code const statusCode = error.statusCode || this.options.customErrorCodes[error.name] || 500; // Send error response reply.code(statusCode).send(errorResponse); }; } /** * Register error handler with Fastify instance */ register(app) { app.setErrorHandler(this.handler()); } /** * Create validation error handler */ static validationErrorHandler() { return async (error, request, reply) => { if (error.validation) { const validationError = { error: "ValidationError", message: "Request validation failed", statusCode: 400, details: error.validation, requestId: request.id, timestamp: new Date().toISOString(), }; logger.warn("Validation error", { error: error.message, validation: error.validation, method: request.method, url: request.url, requestId: request.id, }); return reply.code(400).send(validationError); } // Fall back to default error handling throw error; }; } /** * Create not found handler */ static notFoundHandler() { return async (request, reply) => { const notFoundError = { error: "NotFound", message: `Route ${request.method} ${request.url} not found`, statusCode: 404, requestId: request.id, timestamp: new Date().toISOString(), }; logger.warn("Route not found", { method: request.method, url: request.url, requestId: request.id, }); reply.code(404).send(notFoundError); }; } } export function createFastifyErrorHandler(options) { return new FastifyErrorHandler(options); } // Custom error classes for better error handling export class ApiError extends Error { statusCode; code; constructor(message, statusCode = 500, code) { super(message); this.statusCode = statusCode; this.code = code; this.name = "ApiError"; } } export class ValidationError extends ApiError { details; constructor(message, details) { super(message, 400, "VALIDATION_ERROR"); this.details = details; this.name = "ValidationError"; } } export class AuthenticationError extends ApiError { constructor(message = "Authentication required") { super(message, 401, "AUTHENTICATION_ERROR"); this.name = "AuthenticationError"; } } export class AuthorizationError extends ApiError { constructor(message = "Insufficient permissions") { super(message, 403, "AUTHORIZATION_ERROR"); this.name = "AuthorizationError"; } } export class NotFoundError extends ApiError { constructor(message = "Resource not found") { super(message, 404, "NOT_FOUND_ERROR"); this.name = "NotFoundError"; } } export class ConflictError extends ApiError { constructor(message = "Resource conflict") { super(message, 409, "CONFLICT_ERROR"); this.name = "ConflictError"; } } export class RateLimitError extends ApiError { constructor(message = "Rate limit exceeded") { super(message, 429, "RATE_LIMIT_ERROR"); this.name = "RateLimitError"; } } //# sourceMappingURL=fastify-error-handler.js.map