diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-12-06 17:05:21 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-12-06 17:37:04 +0900 |
| commit | 811458427593a4172a2cd535cc768db375350dca (patch) | |
| tree | 6c4f46c96b6f29392dc19d591e39e03c187033a1 /src/server/middleware/error-handler.ts | |
| parent | 9736a8981fbd6c6defbd67517ca23904fc844629 (diff) | |
| download | kioku-811458427593a4172a2cd535cc768db375350dca.tar.gz kioku-811458427593a4172a2cd535cc768db375350dca.tar.zst kioku-811458427593a4172a2cd535cc768db375350dca.zip | |
feat(dev): change architecture and directory structure
Diffstat (limited to 'src/server/middleware/error-handler.ts')
| -rw-r--r-- | src/server/middleware/error-handler.ts | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/server/middleware/error-handler.ts b/src/server/middleware/error-handler.ts new file mode 100644 index 0000000..7b92940 --- /dev/null +++ b/src/server/middleware/error-handler.ts @@ -0,0 +1,95 @@ +import type { Context } from "hono"; +import { HTTPException } from "hono/http-exception"; +import type { ContentfulStatusCode } from "hono/utils/http-status"; + +/** + * Application-specific error with status code and optional details + */ +export class AppError extends Error { + readonly statusCode: ContentfulStatusCode; + readonly code: string; + + constructor( + message: string, + statusCode: ContentfulStatusCode = 500, + code = "INTERNAL_ERROR", + ) { + super(message); + this.name = "AppError"; + this.statusCode = statusCode; + this.code = code; + } +} + +/** + * Common error factory functions + */ +export const Errors = { + badRequest: (message = "Bad request", code = "BAD_REQUEST") => + new AppError(message, 400, code), + + unauthorized: (message = "Unauthorized", code = "UNAUTHORIZED") => + new AppError(message, 401, code), + + forbidden: (message = "Forbidden", code = "FORBIDDEN") => + new AppError(message, 403, code), + + notFound: (message = "Not found", code = "NOT_FOUND") => + new AppError(message, 404, code), + + conflict: (message = "Conflict", code = "CONFLICT") => + new AppError(message, 409, code), + + validationError: (message = "Validation failed", code = "VALIDATION_ERROR") => + new AppError(message, 422, code), + + internal: (message = "Internal server error", code = "INTERNAL_ERROR") => + new AppError(message, 500, code), +}; + +interface ErrorResponse { + error: { + message: string; + code: string; + }; +} + +/** + * Global error handler middleware for Hono + */ +export function errorHandler(err: Error, c: Context): Response { + // Handle AppError + if (err instanceof AppError) { + const response: ErrorResponse = { + error: { + message: err.message, + code: err.code, + }, + }; + return c.json(response, err.statusCode); + } + + // Handle Hono's HTTPException + if (err instanceof HTTPException) { + const response: ErrorResponse = { + error: { + message: err.message, + code: "HTTP_ERROR", + }, + }; + return c.json(response, err.status as ContentfulStatusCode); + } + + // Handle unknown errors + console.error("Unhandled error:", err); + const response: ErrorResponse = { + error: { + message: + process.env.NODE_ENV === "production" + ? "Internal server error" + : err.message, + code: "INTERNAL_ERROR", + }, + }; + return c.json(response, 500); +} |
