@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
JavaScript
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