mcpField
I
inngest-middleware

inngest-middleware

github.com/joelhooks開発6536 回インストール2 日前に更新
リポジトリ

inngest-middleware について

TypeScript で Inngest を使う開発者向けのミドルウェアガイドです。依存性注入、エラー追跡、データ変換などを扱うライフサイクルフックを説明します。

主な機能

  1. 01クライアント単位・関数単位のミドルウェア
  2. 02依存性注入
  3. 03暗号化と Sentry によるエラー追跡
  4. 04メトリクスとパフォーマンス追跡
  5. 05ミドルウェア動作のテスト

活用シーン

  1. 01関数へのログとメトリクスの追加
  2. 02関数間でのクライアントインスタンス共有
  3. 03ステップデータの暗号化と例外追跡

インストール

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

$npx skills add https://github.com/joelhooks/joelclaw --skill inngest-middleware

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

詳細

Inngest ミドルウェア

ログ記録、エラー追跡、依存性注入、データ変換など、横断的な関心事に対応する Inngest ミドルウェアについて学びましょう。ミドルウェアは関数のライフサイクルにおける重要なタイミングで実行され、可観測性や共通機能のための強力なパターンを実現します。

このスキルは TypeScript に特化しています。 Python または Go については、言語固有のガイダンスを記載した Inngest ドキュメントを参照してください。基本概念はすべての言語に共通します。

ミドルウェアとは?

ミドルウェアを使うと、Inngest クライアントのライフサイクルにおけるさまざまなタイミングでコードを実行できます。関数の実行中やイベント送信中などが該当します。ミドルウェアは、Inngest の実行パイプラインにフックを追加するものと考えてください。

ミドルウェアを使用する場面:

  • 可観測性: ログ、トレーシング、メトリクスを追加する
  • 依存性注入: 関数間でクライアントインスタンスを共有する
  • データ変換: データの暗号化・復号、検証、拡充を行う
  • エラー処理: エラー追跡とアラートをカスタマイズする
  • 認証: ユーザーコンテキストや権限を検証する

ミドルウェアのライフサイクル

ミドルウェアはクライアントレベル(すべての関数に適用)または関数レベル(特定の関数に適用)で登録できます。

実行順序
const inngest = new Inngest({
  id: "my-app",
  middleware: [
    loggingMiddleware, // Runs 1st
    errorMiddleware // Runs 2nd
  ]
});

inngest.createFunction(
  {
    id: "example",
    middleware: [
      authMiddleware, // Runs 3rd
      metricsMiddleware // Runs 4th
    ]
  },
  { event: "test" },
  async () => {
    /* function code */
  }
);

順序が重要です: クライアントミドルウェアが最初に実行され、その後、指定された順序で関数ミドルウェアが実行されます。

カスタムミドルウェアの作成

基本的なミドルウェアの構造
import { InngestMiddleware } from "inngest";

const loggingMiddleware = new InngestMiddleware({
  name: "Logging Middleware",
  init() {
    // Setup phase - runs when client initializes
    const logger = setupLogger();

    return {
      // Function execution lifecycle
      // Note: `fn` is loosely typed in middleware generics; fn.id works at runtime
      onFunctionRun({ ctx, fn }) {
        return {
          beforeExecution() {
            logger.info("Function starting", {
              functionId: fn.id,
              eventName: ctx.event.name,
              runId: ctx.runId
            });
          },

          afterExecution() {
            logger.info("Function completed", {
              functionId: fn.id,
              runId: ctx.runId
            });
          },

          transformOutput({ result }) {
            // Log function output
            logger.debug("Function output", {
              functionId: fn.id,
              output: result.data
            });

            // Return unmodified result
            return { result };
          }
        };
      },

      // Event sending lifecycle
      onSendEvent() {
        return {
          transformInput({ payloads }) {
            logger.info("Sending events", {
              count: payloads.length,
              events: payloads.map((p) => p.name)
            });

            // Spread to convert readonly array to mutable array
            return { payloads: [...payloads] };
          }
        };
      }
    };
  }
});
Python での実装

Python のミドルウェアも同様のパターンに従います。Python の完全な例については、依存性注入リファレンスを参照してください。


## Dependency Injection

Share expensive or stateful clients across all functions. **See [Dependency Injection Reference](./references/dependency-injection.md) for detailed patterns.**

### Quick Example - Built-in DI

```typescript
import { dependencyInjectionMiddleware } from "inngest";

const inngest = new Inngest({
  id: 'my-app',
  middleware: [
    dependencyInjectionMiddleware({
      openai: new OpenAI(),
      db: new PrismaClient(),
    }),
  ],
});

// Functions automatically get injected dependencies
inngest.createFunction(
  { id: "ai-summary" },
  { event: "document/uploaded" },
  async ({ event, openai, db }) => {
    // Dependencies available in function context
    const summary = await openai.chat.completions.create({
      messages: [{ role: "user", content: event.data.content }],
      model: "gpt-4",
    });

    await db.document.update({
      where: { id: event.data.documentId },
      data: { summary: summary.choices[0].message.content }
    });
  }
);

ミドルウェアパッケージ

dependencyInjectionMiddleware(組み込み、上記参照)のほかに、Inngest は公式ミドルウェアを個別のパッケージとして提供しています。詳細については、ミドルウェアリファレンスを参照してください。

暗号化ミドルウェア
npm install @inngest/middleware-encryption
import { encryptionMiddleware } from "@inngest/middleware-encryption";

const inngest = new Inngest({
  id: "my-app",
  middleware: [
    encryptionMiddleware({
      key: process.env.ENCRYPTION_KEY,
    })
  ]
});

すべてのステップデータ、関数の出力、イベントの data.encrypted フィールドを自動的に暗号化します。fallbackDecryptionKeys による鍵のローテーションに対応しています。

Sentry によるエラー追跡
npm install @inngest/middleware-sentry
import * as Sentry from "@sentry/node";
import { sentryMiddleware } from "@inngest/middleware-sentry";

Sentry.init({ /* your Sentry config */ });

const inngest = new Inngest({
  id: "my-app",
  middleware: [sentryMiddleware()]
});

例外を捕捉し、各関数の実行にトレーシングを追加します。また、関数 ID とイベント名をコンテキストとして含めます。@sentry/*@>=8.0.0 が必要です。

よく使われるミドルウェアのパターン

メトリクスとパフォーマンスの追跡
const metricsMiddleware = new InngestMiddleware({
  name: "Metrics Tracking",
  init() {
    return {
      onFunctionRun({ ctx, fn }) {
        let startTime: number;

        return {
          beforeExecution() {
            startTime = Date.now();
            metrics.increment("inngest.step.started", {
              function: fn.id,
              event: ctx.event.name
            });
          },

          afterExecution() {
            const duration = Date.now() - startTime;
            metrics.histogram("inngest.step.duration", duration, {
              function: fn.id,
              event: ctx.event.name
            });
          },

          transformOutput({ result }) {
            const status = result.error ? "error" : "success";
            metrics.increment("inngest.step.completed", {
              function: fn.id,
              status: status
            });

            return { result };
          }
        };
      }
    };
  }
});
高度なパターン

認証: トークンを検証し、ユーザーコンテキストを注入します
条件付きロジック: イベントの種類や関数に応じてミドルウェアを適用します
サーキットブレーカー: 外部サービスによる連鎖的な障害を防ぎます

設定ベースのミドルウェア

さまざまな環境やユースケースに対応する設定オプションを備えた、再利用可能なミドルウェアを作成します。完全な例については、リファレンスドキュメントを参照してください。

ベストプラクティス

設計原則
  1. ミドルウェアの役割を絞ります: 1 つのミドルウェアにつき、扱う関心事は 1 つにします
  2. 障害時のポリシーを選びます: 任意のテレメトリーは、段階的に機能を縮退させてもかまいません。認証、認可、検証、暗号化で障害が発生した場合は、必ずフェイルクローズにします
  3. パフォーマンスを考慮します: ミドルウェアは実行のたびに動作します
  4. 適切な型を使用します: TypeScript にミドルウェアの型を推論させます
  5. 十分にテストします: ミドルウェアは、それを使用するすべての関数に影響します
実装する一般的なユースケース
  • 一時的な障害に対応するリトライロジック
  • 外部サービス呼び出し向けのサーキットブレーカー
  • デバッグ用のリクエスト/レスポンスのログ記録
  • 外部ソースからのユーザーコンテキストの拡充
  • 段階的なロールアウトのためのフィーチャーフラグ
  • カスタム認証および認可チェック
ミドルウェアでのエラー処理
const optionalTelemetryMiddleware = new InngestMiddleware({
  name: "Optional Telemetry Middleware",
  init() {
    return {
      onFunctionRun({ ctx, fn }) {
        return {
          transformOutput({ result }) {
            try {
              // Your middleware logic here
              return performTransformation(result);
            } catch (middlewareError) {
              // This example is optional telemetry only; required guards must throw.
              console.error("Middleware error:", middlewareError);

              // Optional telemetry only. Security/validation middleware must propagate failure.
              return { result };
            }
          }
        };
      }
    };
  }
});
ミドルウェアのテスト

Inngest のテストユーティリティ(createMockContext、createMockFunction)を使用して、ミドルウェアの動作を単体テストします。

完全な実装例と高度なパターンについては、次を参照してください:

コメント