aboutsummaryrefslogtreecommitdiffhomepage
path: root/pkgs/server/src/middleware/error-handler.ts
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-11-30 06:39:51 +0900
committernsfisis <nsfisis@gmail.com>2025-12-04 23:26:11 +0900
commit442c60b8f92499c45076c3c4cc1a1472b2dd8098 (patch)
tree67da31227d316a7d7ce5de1abd7c29b865279849 /pkgs/server/src/middleware/error-handler.ts
parent64a1b249d25fe52df9ad7d8c034e2e004354ecd5 (diff)
downloadkioku-442c60b8f92499c45076c3c4cc1a1472b2dd8098.tar.gz
kioku-442c60b8f92499c45076c3c4cc1a1472b2dd8098.tar.zst
kioku-442c60b8f92499c45076c3c4cc1a1472b2dd8098.zip
feat(server): add error handling middleware
Add global error handling middleware for Hono with: - AppError class for application-specific errors with status codes - Errors factory for common HTTP errors (badRequest, unauthorized, etc.) - Consistent JSON error response format - Tests covering all error types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'pkgs/server/src/middleware/error-handler.ts')
-rw-r--r--pkgs/server/src/middleware/error-handler.ts95
1 files changed, 95 insertions, 0 deletions
diff --git a/pkgs/server/src/middleware/error-handler.ts b/pkgs/server/src/middleware/error-handler.ts
new file mode 100644
index 0000000..7b92940
--- /dev/null
+++ b/pkgs/server/src/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);
+}