All files / lib api-error.ts

0% Statements 0/23
0% Branches 0/24
0% Functions 0/3
0% Lines 0/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144                                                                                                                                                                                                                                                                                               
import { NextResponse } from "next/server";
 
/**
 * APIエラークラス
 * 共通のエラーハンドリングのための拡張Errorクラス
 */
export class APIError extends Error {
  constructor(
    public code: string,
    message: string,
    public status: number,
    public details?: unknown
  ) {
    super(message);
    this.name = "APIError";
  }
}
 
/**
 * 定義済みエラーコード
 */
export const ErrorCodes = {
  // 認証・認可エラー
  UNAUTHORIZED: { code: "UNAUTHORIZED", message: "認証が必要です", status: 401 },
  FORBIDDEN: { code: "FORBIDDEN", message: "この操作を実行する権限がありません", status: 403 },
 
  // リソースエラー
  NOT_FOUND: { code: "NOT_FOUND", message: "リソースが見つかりません", status: 404 },
  ALREADY_EXISTS: { code: "ALREADY_EXISTS", message: "既に存在しています", status: 409 },
 
  // バリデーションエラー
  VALIDATION_ERROR: { code: "VALIDATION_ERROR", message: "入力値が不正です", status: 400 },
  INVALID_REQUEST: { code: "INVALID_REQUEST", message: "リクエストが不正です", status: 400 },
 
  // サーバーエラー
  INTERNAL_ERROR: { code: "INTERNAL_ERROR", message: "サーバーエラーが発生しました", status: 500 },
  DATABASE_ERROR: { code: "DATABASE_ERROR", message: "データベースエラーが発生しました", status: 500 },
 
  // レート制限
  RATE_LIMIT_EXCEEDED: { code: "RATE_LIMIT_EXCEEDED", message: "レート制限を超えました", status: 429 },
} as const;
 
/**
 * APIエラーハンドラー
 * エラーを適切なNextResponseに変換する
 *
 * @param error - エラーオブジェクト
 * @returns NextResponse
 */
export function handleAPIError(error: unknown): NextResponse {
  // APIErrorの場合
  if (error instanceof APIError) {
    return NextResponse.json(
      {
        error: {
          code: error.code,
          message: error.message,
          details: error.details,
        },
      },
      { status: error.status }
    );
  }
 
  // Zodバリデーションエラーの場合
  if (error && typeof error === "object" && "name" in error && error.name === "ZodError") {
    const zodError = error as unknown as { issues: Array<{ path: (string | number)[]; message: string }> };
    return NextResponse.json(
      {
        error: {
          code: ErrorCodes.VALIDATION_ERROR.code,
          message: ErrorCodes.VALIDATION_ERROR.message,
          details: zodError.issues,
        },
      },
      { status: ErrorCodes.VALIDATION_ERROR.status }
    );
  }
 
  // Mongooseバリデーションエラーの場合
  if (error && typeof error === "object" && "name" in error && error.name === "ValidationError") {
    return NextResponse.json(
      {
        error: {
          code: ErrorCodes.VALIDATION_ERROR.code,
          message: ErrorCodes.VALIDATION_ERROR.message,
          details: error,
        },
      },
      { status: ErrorCodes.VALIDATION_ERROR.status }
    );
  }
 
  // Mongoose CastErrorの場合(無効なObjectId等)
  if (error && typeof error === "object" && "name" in error && error.name === "CastError") {
    return NextResponse.json(
      {
        error: {
          code: ErrorCodes.INVALID_REQUEST.code,
          message: "無効なIDが指定されました",
          details: error,
        },
      },
      { status: ErrorCodes.INVALID_REQUEST.status }
    );
  }
 
  // 一般的なErrorの場合
  if (error instanceof Error) {
    console.error("Unhandled error:", error);
    return NextResponse.json(
      {
        error: {
          code: ErrorCodes.INTERNAL_ERROR.code,
          message: process.env.NODE_ENV === "development" ? error.message : ErrorCodes.INTERNAL_ERROR.message,
        },
      },
      { status: ErrorCodes.INTERNAL_ERROR.status }
    );
  }
 
  // その他の未知のエラー
  console.error("Unknown error:", error);
  return NextResponse.json(
    {
      error: {
        code: ErrorCodes.INTERNAL_ERROR.code,
        message: ErrorCodes.INTERNAL_ERROR.message,
      },
    },
    { status: ErrorCodes.INTERNAL_ERROR.status }
  );
}
 
/**
 * ヘルパー関数: 定義済みエラーを生成
 */
export function createError(
  errorCode: typeof ErrorCodes[keyof typeof ErrorCodes],
  details?: unknown
): APIError {
  return new APIError(errorCode.code, errorCode.message, errorCode.status, details);
}