Advanced

Error Handling Design Patterns with Claude Code

Learn about error handling design patterns using Claude Code. Practical tips and code examples included.

エラーハンドリングの設計をClaude Codeで改善する

適切なエラーハンドリングはアプリケーションの堅牢性に直結します。Claude Codeを使えば、型安全で一貫性のあるエラーハンドリングパターンを効率的に実装できます。

カスタムエラークラスの設計

> アプリケーション全体で使うカスタムエラークラスを設計して。
> HTTPステータスコードとの対応、エラーコード、
> ユーザー向けメッセージを含めて。
// errors/base.ts
export abstract class AppError extends Error {
  abstract readonly statusCode: number;
  abstract readonly code: string;
  readonly isOperational: boolean;

  constructor(message: string, isOperational = true) {
    super(message);
    this.name = this.constructor.name;
    this.isOperational = isOperational;
    Error.captureStackTrace(this, this.constructor);
  }

  toJSON() {
    return {
      error: {
        code: this.code,
        message: this.message,
        ...(process.env.NODE_ENV === "development" && { stack: this.stack }),
      },
    };
  }
}

// errors/http.ts
export class NotFoundError extends AppError {
  readonly statusCode = 404;
  readonly code = "NOT_FOUND";

  constructor(resource: string, id?: string) {
    super(id ? `${resource}(${id})が見つかりません` : `${resource}が見つかりません`);
  }
}

export class ValidationError extends AppError {
  readonly statusCode = 400;
  readonly code = "VALIDATION_ERROR";
  readonly fields: Record<string, string[]>;

  constructor(fields: Record<string, string[]>) {
    super("バリデーションエラーがあります");
    this.fields = fields;
  }

  toJSON() {
    return {
      error: {
        code: this.code,
        message: this.message,
        fields: this.fields,
      },
    };
  }
}

export class UnauthorizedError extends AppError {
  readonly statusCode = 401;
  readonly code = "UNAUTHORIZED";
  constructor(message = "Authentication required") {
    super(message);
  }
}

export class ForbiddenError extends AppError {
  readonly statusCode = 403;
  readonly code = "FORBIDDEN";
  constructor(message = "権限がありません") {
    super(message);
  }
}

Result型パターン

例外を使わずにエラーを型安全に扱うResult型パターンです。

> Result型パターンを実装して。
> 成功と失敗を型レベルで区別できるように。
// types/result.ts
type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

function ok<T>(data: T): Result<T, never> {
  return { success: true, data };
}

function err<E>(error: E): Result<never, E> {
  return { success: false, error };
}

// Usage example
async function findUser(id: string): Promise<Result<User, NotFoundError>> {
  const user = await db.user.findUnique({ where: { id } });
  if (!user) {
    return err(new NotFoundError("User", id));
  }
  return ok(user);
}

// 呼び出し側
const result = await findUser("123");
if (result.success) {
  console.log(result.data.name); // 型安全にアクセス
} else {
  console.log(result.error.message); // エラー型も型安全
}

グローバルエラーハンドラー

Express用のグローバルエラーハンドラーを実装します。

> Expressのグローバルエラーハンドラーミドルウェアを作成して。
> AppErrorは適切なレスポンスに変換、予期しないエラーは500に。
> 本番環境ではスタックトレースを隠す。
import { Request, Response, NextFunction } from "express";
import { AppError } from "../errors/base";

export function globalErrorHandler(
  err: Error,
  req: Request,
  res: Response,
  _next: NextFunction
) {
  // ログ出力
  if (err instanceof AppError && err.isOperational) {
    console.warn(`[${err.code}] ${err.message}`);
  } else {
    console.error("Unexpected error:", err);
  }

  // AppErrorの場合は定義されたレスポンスを返す
  if (err instanceof AppError) {
    return res.status(err.statusCode).json(err.toJSON());
  }

  // 予期しないエラー
  res.status(500).json({
    error: {
      code: "INTERNAL_ERROR",
      message: process.env.NODE_ENV === "production"
        ? "サーバーエラーが発生しました"
        : err.message,
    },
  });
}

非同期エラーのキャッチ

> Express のasync route handlerのエラーを
> 自動キャッチするラッパーを作成して。
import { Request, Response, NextFunction, RequestHandler } from "express";

function asyncHandler(
  fn: (req: Request, res: Response, next: NextFunction) => Promise<void>
): RequestHandler {
  return (req, res, next) => {
    fn(req, res, next).catch(next);
  };
}

// Usage example:try-catchが不要に
router.get("/users/:id", asyncHandler(async (req, res) => {
  const user = await userService.findById(req.params.id);
  if (!user) {
    throw new NotFoundError("User", req.params.id);
  }
  res.json({ data: user });
}));

フロントエンドのエラーバウンダリ

> React Error Boundary を作成して。
> エラーの種類に応じて異なるUIを表示して。
import { Component, ReactNode } from "react";

interface Props {
  children: ReactNode;
  fallback?: (error: Error, retry: () => void) => ReactNode;
}

interface State {
  error: Error | null;
}

class ErrorBoundary extends Component<Props, State> {
  state: State = { error: null };

  static getDerivedStateFromError(error: Error) {
    return { error };
  }

  handleRetry = () => {
    this.setState({ error: null });
  };

  render() {
    if (this.state.error) {
      if (this.props.fallback) {
        return this.props.fallback(this.state.error, this.handleRetry);
      }
      return (
        <div className="p-8 text-center">
          <h2 className="text-xl font-bold text-red-600">エラーが発生しました</h2>
          <p className="mt-2 text-gray-600">{this.state.error.message}</p>
          <button
            onClick={this.handleRetry}
            className="mt-4 rounded bg-blue-500 px-4 py-2 text-white"
          >
            再試行
          </button>
        </div>
      );
    }
    return this.props.children;
  }
}

デバッグとエラー調査の具体的な方法はデバッグテクニック完全ガイドを、TypeScriptでの型設計はTypeScript開発での活用法を参照してください。セキュリティの観点からのエラー情報の扱い方はセキュリティ監査の自動化もあわせてご覧ください。

Summary

一貫したエラーハンドリング設計により、アプリケーションの堅牢性と保守性が大幅に向上します。Claude Codeを使えば、カスタムエラークラス、Result型、グローバルハンドラーを効率的に実装できます。

エラーハンドリングのベストプラクティスはNode.js公式ガイド、Claude CodeについてはAnthropic公式ドキュメントを参照してください。

#Claude Code #error handling #design patterns #TypeScript #robustness