关于 inngest-middleware
面向 TypeScript 的 Inngest 中间件技能,用于处理函数执行和事件处理中的通用需求,涵盖生命周期钩子、依赖注入、加密与 Sentry 错误跟踪。
主要功能
- 01添加生命周期钩子
- 02注入共享依赖项
- 03加密步骤和事件数据
- 04使用 Sentry 跟踪错误
- 05测试中间件行为
适用场景
- 01为函数添加日志或指标
- 02在函数间共享客户端
- 03加密函数数据
- 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 };
}
};
}
};
}
});
高级模式
身份验证: 验证令牌并注入用户上下文
条件逻辑: 根据事件类型或函数应用中间件
熔断器: 防止外部服务故障级联
基于配置的中间件
创建带有配置选项的可复用中间件,以适应不同环境和使用场景。完整示例请参阅参考文档。
最佳实践
设计原则
- 让中间件专注于单一职责: 每个中间件只处理一项关注点
- 妥善处理错误: 不要让中间件导致函数崩溃
- 考虑性能: 中间件会在每次执行时运行
- 使用正确的类型: 让 TypeScript 推断中间件类型
- 充分测试: 中间件会影响所有使用它的函数
常见实现用例
- 针对临时故障的重试逻辑
- 针对外部服务调用的熔断器
- 用于调试的请求/响应日志记录
- 从外部来源丰富用户上下文
- 用于逐步推出的功能标记
- 自定义身份验证和授权检查
中间件中的错误处理
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)对中间件行为进行单元测试。
完整实现示例和高级模式,请参阅: