148 lines
3.1 KiB
TypeScript
148 lines
3.1 KiB
TypeScript
|
|
/**
|
||
|
|
* Error handling utilities for The Order services
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { FastifyError, FastifyReply, FastifyRequest } from 'fastify';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Custom application error class
|
||
|
|
*/
|
||
|
|
export class AppError extends Error {
|
||
|
|
constructor(
|
||
|
|
public statusCode: number,
|
||
|
|
public code: string,
|
||
|
|
message: string,
|
||
|
|
public details?: unknown,
|
||
|
|
public retryable = false,
|
||
|
|
public timestamp = new Date()
|
||
|
|
) {
|
||
|
|
super(message);
|
||
|
|
this.name = 'AppError';
|
||
|
|
Error.captureStackTrace(this, this.constructor);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if error is retryable
|
||
|
|
*/
|
||
|
|
isRetryable(): boolean {
|
||
|
|
return this.retryable;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get error context
|
||
|
|
*/
|
||
|
|
getContext(): Record<string, unknown> {
|
||
|
|
return {
|
||
|
|
statusCode: this.statusCode,
|
||
|
|
code: this.code,
|
||
|
|
message: this.message,
|
||
|
|
details: this.details,
|
||
|
|
retryable: this.retryable,
|
||
|
|
timestamp: this.timestamp.toISOString(),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Global error handler for Fastify
|
||
|
|
*/
|
||
|
|
export async function errorHandler(
|
||
|
|
error: FastifyError,
|
||
|
|
request: FastifyRequest,
|
||
|
|
reply: FastifyReply
|
||
|
|
): Promise<void> {
|
||
|
|
request.log.error({
|
||
|
|
err: error,
|
||
|
|
url: request.url,
|
||
|
|
method: request.method,
|
||
|
|
statusCode: error.statusCode || 500,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (error instanceof AppError) {
|
||
|
|
return reply.status(error.statusCode).send({
|
||
|
|
error: {
|
||
|
|
code: error.code,
|
||
|
|
message: error.message,
|
||
|
|
details: error.details,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle validation errors
|
||
|
|
if (error.validation) {
|
||
|
|
return reply.status(400).send({
|
||
|
|
error: {
|
||
|
|
code: 'VALIDATION_ERROR',
|
||
|
|
message: 'Validation failed',
|
||
|
|
details: error.validation,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Don't expose internal errors in production
|
||
|
|
const isProduction = process.env.NODE_ENV === 'production';
|
||
|
|
const statusCode = error.statusCode || 500;
|
||
|
|
|
||
|
|
// Log error details for monitoring
|
||
|
|
request.log.error({
|
||
|
|
error: {
|
||
|
|
message: error.message,
|
||
|
|
stack: error.stack,
|
||
|
|
code: error.code || 'INTERNAL_ERROR',
|
||
|
|
statusCode,
|
||
|
|
},
|
||
|
|
request: {
|
||
|
|
method: request.method,
|
||
|
|
url: request.url,
|
||
|
|
headers: request.headers,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
return reply.status(statusCode).send({
|
||
|
|
error: {
|
||
|
|
code: 'INTERNAL_ERROR',
|
||
|
|
message: isProduction ? 'Internal server error' : error.message,
|
||
|
|
...(isProduction ? {} : { stack: error.stack }),
|
||
|
|
...(error instanceof AppError && error.retryable ? { retryable: true } : {}),
|
||
|
|
},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create a standardized error response
|
||
|
|
*/
|
||
|
|
export function createErrorResponse(
|
||
|
|
statusCode: number,
|
||
|
|
code: string,
|
||
|
|
message: string,
|
||
|
|
details?: unknown,
|
||
|
|
retryable = false
|
||
|
|
): AppError {
|
||
|
|
return new AppError(statusCode, code, message, details, retryable);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create a retryable error
|
||
|
|
*/
|
||
|
|
export function createRetryableError(
|
||
|
|
statusCode: number,
|
||
|
|
code: string,
|
||
|
|
message: string,
|
||
|
|
details?: unknown
|
||
|
|
): AppError {
|
||
|
|
return new AppError(statusCode, code, message, details, true);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create a non-retryable error
|
||
|
|
*/
|
||
|
|
export function createNonRetryableError(
|
||
|
|
statusCode: number,
|
||
|
|
code: string,
|
||
|
|
message: string,
|
||
|
|
details?: unknown
|
||
|
|
): AppError {
|
||
|
|
return new AppError(statusCode, code, message, details, false);
|
||
|
|
}
|
||
|
|
|