jpskill.com
🛠️ 開発・MCP コミュニティ

inngest-handler

信頼性の高いバックグラウンド処理、ワークフロー、スケジュールされたタスクのために、Inngest関数を作成・管理し、ビジネスにおける様々な処理を効率化・自動化するSkill。

📜 元の英語説明(参考)

Create and manage Inngest functions for reliable background jobs, workflows, and scheduled tasks.

🇯🇵 日本人クリエイター向け解説

一言でいうと

信頼性の高いバックグラウンド処理、ワークフロー、スケジュールされたタスクのために、Inngest関数を作成・管理し、ビジネスにおける様々な処理を効率化・自動化するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o inngest-handler.zip https://jpskill.com/download/16909.zip && unzip -o inngest-handler.zip && rm inngest-handler.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16909.zip -OutFile "$d\inngest-handler.zip"; Expand-Archive "$d\inngest-handler.zip" -DestinationPath $d -Force; ri "$d\inngest-handler.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して inngest-handler.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → inngest-handler フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Inngest Function Handler Skill

この Skill は、Inngest を使用して耐久性のある、複数ステップのワークフローを構築するための標準を定義します。

🚨 厳守事項 (厳密に従ってください)

  1. setTimeout / setInterval を使用しない:

    • 悪い例: await new Promise(r => setTimeout(r, 1000))
    • 良い例: await step.sleep("wait-1s", "1s")
    • 理由: サーバーレス関数はタイムアウトします。Inngest のスリープは最大 1 年間持続します。
  2. ステップ外で副作用を起こさない:

    • データベースへの書き込み、API 呼び出し、または非決定的なロジック (ランダム、日付) は、必ず step.run() でラップする必要があります。
    • 理由: Inngest 関数は複数回実行されます (メモ化)。ステップ外のコードは毎回実行されます。
  3. 決定的なステップ:

    • ステップは ID (最初の引数) によってメモ化されます。ID は一意で安定している必要があります。
    • ステップ ID を動的に生成しないでください (インデックス付きのループ内など、特別な理由がない限り)。
  4. ステップからデータを返す:

    • 後で値が必要な場合は、ステップから返します。
    • 悪い例: let userId; await step.run(..., () => { userId = ... })
    • 良い例: const userId = await step.run(..., () => { return ... })

コアパターン

1. 複数ステップの実行

すべてのロジックをステップでラップして、再試行可能性と再開可能性を確保します。

export const processOrder = inngest.createFunction(
  { id: "process-order" },
  { event: "shop/order.created" },
  async ({ event, step }) => {
    // 1. Step: Validate (再試行可能)
    const user = await step.run("get-user", async () => {
      return await db.users.findById(event.data.userId);
    });

    // 2. Step: Sleep (耐久性のある一時停止)
    await step.sleep("wait-for-payment", "1h");

    // 3. Step: Wait for Event (人間/システムとのインタラクション)
    const payment = await step.waitForEvent("wait-payment", {
      event: "shop/payment.success",
      match: "data.orderId",
      timeout: "24h"
    });

    // 4. Step: 条件付きロジック
    if (!payment) {
        await step.run("cancel-order", async () => { ... });
    }
  }
);

2. 並列処理

ステップを同時に実行して、実行を高速化します。

const [user, subscription] = await Promise.all([
  step.run("fetch-user", () => db.users.find(...)),
  step.run("fetch-sub", () => stripe.subscriptions.retrieve(...))
]);

3. ループの処理

ループ内では、ステップ ID が一意であることを確認します。

const items = event.data.items;
for (const item of items) {
  // アイテムごとに一意性を確保するために動的な ID を使用します
  await step.run(`process-item-${item.id}`, async () => {
    await processItem(item);
    });
}

構成とフロー制御

レート制限とスロットリング

サードパーティ API への過負荷を防ぎます。

inngest.createFunction({
    id: "sync-crm",
    // ユーザーあたり 1 分あたり最大 10 リクエスト
    rateLimit: { limit: 10, period: "1m", key: "event.data.userId" },
    // キューがいっぱいの場合はイベントをドロップします
    throttle: { limit: 5, period: "1s" } 
}, ...);

デバウンス

ウィンドウ内の最新のイベントのみを処理します (例: 検索インデックス作成)。

inngest.createFunction({
    id: "index-product",
    // より多くのイベントを 10 秒間待ちます。最新のデータでのみ実行します
    debounce: { period: "10s", key: "event.data.productId" }
}, ...);

優先度

特定のイベントを優先します (例: 有料ユーザー)。

inngest.createFunction({
    id: "generate-report",
    // 数値が高いほど優先度が高くなります
    priority: { run: "event.data.plan === 'enterprise' ? 100 : 0" }
}, ...);

エラー処理

自動再試行

Inngest は、エラーが発生した場合にステップを自動的に再試行します (デフォルトではバックオフありで約 4 ~ 5 回)。

  • カスタマイズ: config で { retries: 10 }

再試行不可能なエラー

エラーが致命的な場合 (例: 400 Bad Request) は、すぐに実行を停止します。

import { NonRetriableError } from "inngest";

await step.run("validate", async () => {
  if (!isValid) throw new NonRetriableError("Invalid payload");
});

失敗ハンドラー (ロールバック)

すべての再試行後に関数が失敗した場合に、クリーンアップロジックを実行します。

export const riskyFunc = inngest.createFunction(
  { 
    id: "risky-transfer",
    // メインハンドラーが失敗した場合に実行されます
    onFailure: async ({ error, event, step }) => {
      await step.run("rollback-funds", async () => {
        await reverseTransfer(event.data.transferId);
      });
      await step.run("notify-admin", async () => {
        await sendAlert(`Transfer failed: ${error.message}`);
      });
    }
  },
  { event: "bank/transfer.init" },
  async ({ step }) => { /* ... */ }
);

登録

必須: すべての関数は src/lib/inngest/functions/index.ts でインポートおよびエクスポートする必要があります。

📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Inngest Function Handler Skill

This skill defines the standards for building durable, multi-step workflows using Inngest.

🚨 HARD RULES (Strictly Follow)

  1. NO setTimeout / setInterval:

    • Bad: await new Promise(r => setTimeout(r, 1000))
    • Good: await step.sleep("wait-1s", "1s")
    • Reason: Serverless functions time out; Inngest sleeps persist for up to a year.
  2. NO Side Effects Outside Steps:

    • Any database write, API call, or non-deterministic logic (random, date) MUST be wrapped in step.run().
    • Reason: Inngest functions execute multiple times (memoization). Code outside steps runs every time.
  3. Deterministic Steps:

    • Steps are memoized by their ID (1st arg). IDs must be unique and stable.
    • Do not dynamically generate step IDs unless you know what you are doing (e.g., inside loops with index).
  4. Return Data from Steps:

    • If you need a value later, return it from the step.
    • Bad: let userId; await step.run(..., () => { userId = ... })
    • Good: const userId = await step.run(..., () => { return ... })

Core Patterns

1. Multi-Step Execution

Wrap all logic in steps to ensure retriability and resumability.

export const processOrder = inngest.createFunction(
  { id: "process-order" },
  { event: "shop/order.created" },
  async ({ event, step }) => {
    // 1. Step: Validate (Retriable)
    const user = await step.run("get-user", async () => {
      return await db.users.findById(event.data.userId);
    });

    // 2. Step: Sleep (Durable pause)
    await step.sleep("wait-for-payment", "1h");

    // 3. Step: Wait for Event (Human/System interaction)
    const payment = await step.waitForEvent("wait-payment", {
      event: "shop/payment.success",
      match: "data.orderId",
      timeout: "24h"
    });

    // 4. Step: Conditional Logic
    if (!payment) {
        await step.run("cancel-order", async () => { ... });
    }
  }
);

2. Parallelism

Run steps concurrently to speed up execution.

const [user, subscription] = await Promise.all([
  step.run("fetch-user", () => db.users.find(...)),
  step.run("fetch-sub", () => stripe.subscriptions.retrieve(...))
]);

3. Working with Loops

Inside loops, ensure step IDs are unique.

const items = event.data.items;
for (const item of items) {
  // Use dynamic ID to ensure uniqueness per item
  await step.run(`process-item-${item.id}`, async () => {
    await processItem(item);
    });
}

Configuration & Flow Control

Rate Limiting & Throttling

Prevent overwhelming 3rd party APIs.

inngest.createFunction({
    id: "sync-crm",
    // Max 10 requests per minute per user
    rateLimit: { limit: 10, period: "1m", key: "event.data.userId" },
    // Drop events if queue is full
    throttle: { limit: 5, period: "1s" } 
}, ...);

Debounce

Process only the latest event in a window (e.g., search indexing).

inngest.createFunction({
    id: "index-product",
    // Wait 10s for more events; only run with the latest data
    debounce: { period: "10s", key: "event.data.productId" }
}, ...);

Priority

Prioritize specific events (e.g., Paid users).

inngest.createFunction({
    id: "generate-report",
    // High number = High priority
    priority: { run: "event.data.plan === 'enterprise' ? 100 : 0" }
}, ...);

Error Handling

Automatic Retries

Inngest retries steps automatically on error (default ~4-5 times with backoff).

  • Customize: { retries: 10 } in config.

Non-Retriable Errors

Stop execution immediately if the error is fatal (e.g., 400 Bad Request).

import { NonRetriableError } from "inngest";

await step.run("validate", async () => {
  if (!isValid) throw new NonRetriableError("Invalid payload");
});

Failure Handlers (Rollbacks)

Execute cleanup logic if the function fails after all retries.

export const riskyFunc = inngest.createFunction(
  { 
    id: "risky-transfer",
    // Runs if main handler fails
    onFailure: async ({ error, event, step }) => {
      await step.run("rollback-funds", async () => {
        await reverseTransfer(event.data.transferId);
      });
      await step.run("notify-admin", async () => {
        await sendAlert(`Transfer failed: ${error.message}`);
      });
    }
  },
  { event: "bank/transfer.init" },
  async ({ step }) => { /* ... */ }
);

Registration

MANDATORY: All functions must be imported and exported in src/lib/inngest/functions/index.ts.