mcpField
I
inngest-middleware

inngest-middleware

github.com/inngest开发293.8k 次安装4 周前更新
代码仓库

关于 inngest-middleware

面向 TypeScript 的 Inngest 中间件技能,用于处理函数执行和事件处理中的通用需求,涵盖生命周期钩子、依赖注入、加密与 Sentry 错误跟踪。

主要功能

  1. 01添加生命周期钩子
  2. 02注入共享依赖项
  3. 03加密步骤和事件数据
  4. 04使用 Sentry 跟踪错误
  5. 05测试中间件行为

适用场景

  1. 01为函数添加日志或指标
  2. 02在函数间共享客户端
  3. 03加密函数数据
  4. 04捕获异常和跟踪信息

安装

复制下面的命令,在终端中运行。

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

安装后在 Claude Code 中输入/inngest-middleware确认已加载。

附带文件references/built-in-middleware.mdreferences/dependency-injection.md

详细介绍

Inngest 中间件

掌握 Inngest 中间件,以处理日志记录、错误跟踪、依赖注入和数据转换等横切关注点。中间件会在函数生命周期的关键节点运行,从而实现强大的可观测性和共享功能模式。

这些技能专注于 TypeScript。 如需了解 Python 或 Go,请参阅 Inngest 文档中的语言专属指南。核心概念适用于所有语言。

注意: 中间件系统在 v4 中进行了重大重写。此处记录的生命周期钩子对应 v4 API。从 v3 迁移时,请参阅迁移指南,了解破坏性变更的详情。

⚠ 使用 Realtime 时,请使用 inngest-realtime 技能,而不是本技能。 Inngest v3 使用 @inngest/realtime 中的 realtimeMiddleware(),向函数处理程序注入 publish 参数。v4 原生支持 realtime——内置了 step.realtime.publish,无需中间件。请勿在 v4 项目中安装 @inngest/realtime(这是 v3 时代的程序包,运行时会产生 TypeError: Cls is not a constructor)。有关 v4 模式,请参阅 inngest-realtime 技能。

什么是中间件?

中间件允许代码在 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
    ],
    triggers: [{ 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", triggers: [{ 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. 让中间件专注于单一职责: 每个中间件只处理一项关注点
  2. 妥善处理错误: 不要让中间件导致函数崩溃
  3. 考虑性能: 中间件会在每次执行时运行
  4. 使用正确的类型: 让 TypeScript 推断中间件类型
  5. 充分测试: 中间件会影响所有使用它的函数
常见实现用例
  • 针对临时故障的重试逻辑
  • 针对外部服务调用的熔断器
  • 用于调试的请求/响应日志记录
  • 从外部来源丰富用户上下文
  • 用于逐步推出的功能标记
  • 自定义身份验证和授权检查
中间件中的错误处理
const robustMiddleware = new InngestMiddleware({
  name: "Robust Middleware",
  init() {
    return {
      onFunctionRun({ ctx, fn }) {
        return {
          transformOutput({ result }) {
            try {
              // Your middleware logic here
              return performTransformation(result);
            } catch (middlewareError) {
              // Log error but don't break the function
              console.error("Middleware error:", middlewareError);

              // Return original result on middleware failure
              return { result };
            }
          }
        };
      }
    };
  }
});
测试中间件

使用 Inngest 的测试工具(createMockContext、createMockFunction)对中间件行为进行单元测试。

完整实现示例和高级模式,请参阅:

评论

支持 Markdown