mcpField
E
error-handling

error-handling

github.com/pluginagentmarketplace開発260 回インストール43 週間前に更新
リポジトリ

error-handling について

Node.js 開発者向けのエラー処理学習スキルです。カスタム例外、Express ミドルウェア、プロセスレベルの障害に加え、リトライやテスト、トラブルシューティングを扱います。

主な機能

  1. 01型付きカスタムエラークラス
  2. 02同期・非同期エラー処理
  3. 03Express エラーミドルウェア
  4. 04プロセスレベルのハンドラー
  5. 05指数バックオフによるリトライ
  6. 06テストとトラブルシューティング

活用シーン

  1. 01堅牢な Node.js アプリの構築
  2. 02Express API のエラー一元管理
  3. 03DB や外部 API の呼び出し失敗への対応
  4. 04エラー動作と復旧処理のテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pluginagentmarketplace/custom-plugin-nodejs --skill error-handling

インストール後、Claude Code で/error-handlingを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
config.yaml
schema.json
references/
GUIDE.md
PATTERNS.md
scripts/
validate.py

詳細

Node.js のエラーハンドリングスキル

適切に失敗し、有益なフィードバックを提供する、堅牢な Node.js アプリケーションを構築するためのエラーハンドリングパターンを習得します。

クイックスタート

エラーハンドリングは次の 4 つのレイヤーで行います。

  1. カスタムエラークラス - 型付けされた、情報量の多いエラー
  2. Try/Catch パターン - 同期処理と非同期処理のハンドリング
  3. Express ミドルウェア - API エラーの一元管理
  4. プロセスハンドラー - キャッチされない例外

主な概念

カスタムエラークラス
// Base application error
class AppError extends Error {
  constructor(message, statusCode, code) {
    super(message);
    this.statusCode = statusCode;
    this.code = code;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

// Specific error types
class ValidationError extends AppError {
  constructor(message, details = []) {
    super(message, 400, 'VALIDATION_ERROR');
    this.details = details;
  }
}

class NotFoundError extends AppError {
  constructor(resource = 'Resource') {
    super(`${resource} not found`, 404, 'NOT_FOUND');
  }
}

class UnauthorizedError extends AppError {
  constructor(message = 'Authentication required') {
    super(message, 401, 'UNAUTHORIZED');
  }
}

class ForbiddenError extends AppError {
  constructor(message = 'Access denied') {
    super(message, 403, 'FORBIDDEN');
  }
}
非同期エラーハンドリング
// Async wrapper for Express routes
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Usage
router.get('/users/:id', asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) throw new NotFoundError('User');
  res.json(user);
}));
Express エラーミドルウェア
const errorHandler = (err, req, res, next) => {
  // Log error
  logger.error({
    message: err.message,
    stack: err.stack,
    code: err.code,
    url: req.originalUrl
  });

  // Handle operational errors
  if (err.isOperational) {
    return res.status(err.statusCode).json({
      success: false,
      error: {
        message: err.message,
        code: err.code,
        ...(err.details && { details: err.details })
      }
    });
  }

  // Production: hide internal errors
  if (process.env.NODE_ENV === 'production') {
    return res.status(500).json({
      success: false,
      error: { message: 'Internal server error', code: 'INTERNAL_ERROR' }
    });
  }

  // Development: show stack
  res.status(500).json({
    success: false,
    error: { message: err.message, stack: err.stack }
  });
};

学習ロードマップ

初級(1~2 週間)
  • ✅ Error 型を理解する
  • ✅ 同期コードでの try/catch
  • ✅ Promise の .catch() ハンドリング
  • ✅ 基本的な Express エラーミドルウェア
中級(3~4 週間)
  • ✅ カスタムエラークラス
  • ✅ 非同期エラーラッパー
  • ✅ エラーハンドリングの一元管理
  • ✅ Winston によるエラーログ記録
上級(5~6 週間)
  • ✅ 本番環境でのエラー監視
  • ✅ グレースフルシャットダウン
  • ✅ サーキットブレーカーパターン
  • ✅ エラーリカバリー戦略

プロセスレベルのエラーハンドリング

// Uncaught exceptions
process.on('uncaughtException', (error) => {
  console.error('Uncaught Exception:', error);
  logger.fatal({ error }, 'Uncaught exception');
  process.exit(1);
});

// Unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection:', reason);
  logger.error({ reason }, 'Unhandled rejection');
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  console.log('SIGTERM received. Graceful shutdown...');
  server.close(() => console.log('HTTP server closed'));
  await mongoose.connection.close();
  process.exit(0);
});

指数バックオフによる再試行

async function retryOperation(fn, options = {}) {
  const { maxRetries = 3, delay = 1000, backoff = 2 } = options;
  let lastError;

  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      if (attempt === maxRetries) throw error;

      const waitTime = delay * Math.pow(backoff, attempt - 1);
      await new Promise(resolve => setTimeout(resolve, waitTime));
    }
  }
  throw lastError;
}

エラーレスポンス形式

// Success
{ "success": true, "data": { ... } }

// Error
{
  "success": false,
  "error": {
    "message": "User not found",
    "code": "NOT_FOUND",
    "details": [],
    "timestamp": "2024-01-15T10:30:00.000Z"
  }
}

単体テストのテンプレート

describe('Error Handling', () => {
  describe('ValidationError', () => {
    it('should create error with correct properties', () => {
      const error = new ValidationError('Invalid input', [
        { field: 'email', message: 'Required' }
      ]);

      expect(error.statusCode).toBe(400);
      expect(error.code).toBe('VALIDATION_ERROR');
      expect(error.isOperational).toBe(true);
    });
  });

  describe('asyncHandler', () => {
    it('should catch async errors', async () => {
      const mockNext = jest.fn();
      const handler = asyncHandler(async () => {
        throw new Error('Test error');
      });

      await handler({}, {}, mockNext);
      expect(mockNext).toHaveBeenCalledWith(expect.any(Error));
    });
  });
});

トラブルシューティング

問題原因解決策
未処理の拒否に関する警告.catch() がないプロセスハンドラーとローカルの catch を追加する
エラーが気づかれないまま握りつぶされる空の catch ブロックエラーをログに記録するか、再スローする
スタックトレースがないError.captureStackTrace が呼び出されていないAppError 基底クラスを使用する
エラーによるメモリーリークエラー参照を保持しているWeakMap を使用するか、クリーンアップする

使用する場面

次のような場合は、適切なエラーハンドリングを使用してください。

  • 本番環境向けの Node.js アプリケーションを構築する
  • Express で REST API を作成する
  • データベース操作を処理する
  • 外部 API 呼び出しを処理する
  • 再試行ロジックを実装する

関連スキル

  • Express REST API(API エラーレスポンス)
  • 非同期プログラミング(Promise の拒否)
  • テストとデバッグ(エラーのテスト)

リソース

コメント