inngest-v3-v4-migration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseInngest v3 to v4 Migration
Inngest v3 迁移至 v4
Use this skill when the user asks to upgrade Inngest, fix v3/v4 errors, migrate
realtime, or clean up a codebase that has mixed SDK patterns.
Primary reference:
https://www.inngest.com/docs/reference/typescript/v4/migrations/v3-to-v4
This skill is agent-first: detect actual usage first, make mechanical API
changes in a controlled order, then typecheck and run focused tests.
当用户要求升级Inngest、修复v3/v4版本错误、迁移实时功能或清理存在混合SDK模式的代码库时,使用本技能。
本技能以Agent优先:先检测实际使用情况,按可控顺序进行机械API变更,然后进行类型检查并运行针对性测试。
When to Trigger
触发场景
Use this skill for requests like:
- "Upgrade Inngest from v3 to v4"
- "Fix our Inngest v4 migration"
- "We're getting signing key required / cloud mode errors after upgrading"
- "with a string function ID stopped working"
step.invoke - "broke after installing
@inngest/realtime"inngest@4 - "Move from EventSchemas to eventType"
- "Make this existing Inngest repo v4-compatible"
If the user asks for a broad codebase reliability audit first, use
to choose scope, then return here for the v4 changes.
inngest-brownfield-audit以下类型的请求适用本技能:
- "将Inngest从v3升级到v4"
- "修复我们的Inngest v4迁移问题"
- "升级后出现签名密钥必填/云模式错误"
- "使用字符串函数ID的停止工作"
step.invoke - "安装后
inngest@4失效"@inngest/realtime - "从EventSchemas切换到eventType"
- "让现有Inngest仓库兼容v4版本"
如果用户首先要求进行广泛的代码库可靠性审计,请使用确定范围,再返回此处进行v4版本变更。
inngest-brownfield-auditMigration Scan
迁移扫描
Start by locating all Inngest surfaces:
bash
rg -n '"inngest"|"@inngest/realtime"|"@inngest/agent-kit"' package.json **/package.json
rg -n 'new Inngest|EventSchemas|eventType|staticSchema|createFunction\\(|serve\\(|connect\\(|step\\.invoke|referenceFunction|@inngest/realtime|realtimeMiddleware|useInngestSubscription|serveHost|rewriteGatewayEndpoint|logLevel|streaming:|signingKey|signingKeyFallback|baseUrl|INNGEST_DEV|INNGEST_SIGNING_KEY' .Then classify the repo:
- No Inngest yet: use , not this migration skill.
inngest-setup - v3 only: migrate the SDK and all breaking changes together.
- mixed v3/v4: prioritize removing broken v3 APIs from v4 code.
- v4 mostly done: focus on missed runtime gotchas like local dev mode,
serverless , realtime package imports, and string
maxRuntime.step.invoke
Before editing, record:
text
Inngest migration scan:
- Current package versions:
- Client files:
- Serve/connect entrypoints:
- Functions using old trigger syntax:
- EventSchemas usage:
- Realtime v3 package usage:
- step.invoke string IDs:
- Serverless runtime constraints:
- Tests/checks available:首先定位所有Inngest相关代码:
bash
rg -n '"inngest"|"@inngest/realtime"|"@inngest/agent-kit"' package.json **/package.json
rg -n 'new Inngest|EventSchemas|eventType|staticSchema|createFunction\\(|serve\\(|connect\\(|step\\.invoke|referenceFunction|@inngest/realtime|realtimeMiddleware|useInngestSubscription|serveHost|rewriteGatewayEndpoint|logLevel|streaming:|signingKey|signingKeyFallback|baseUrl|INNGEST_DEV|INNGEST_SIGNING_KEY' .然后对仓库进行分类:
- 未使用Inngest:使用,而非本迁移技能。
inngest-setup - 仅使用v3版本:同步迁移SDK及所有破坏性变更。
- 混合使用v3/v4:优先移除v4代码中已失效的v3 API。
- 基本完成v4迁移:关注遗漏的运行时问题,如本地开发模式、无服务器、实时包导入以及字符串形式的
maxRuntime。step.invoke
编辑前记录以下信息:
text
Inngest迁移扫描结果:
- 当前包版本:
- 客户端文件:
- Serve/connect入口:
- 使用旧触发器语法的函数:
- EventSchemas使用情况:
- v3实时包使用情况:
- step.invoke字符串ID:
- 无服务器运行时限制:
- 可用测试/检查项:Upgrade Order
升级步骤
- Update package versions.
- Fix client construction and local/prod mode.
- Move serve options to the client.
- Move triggers into options.
createFunction - Replace with
EventSchemas/eventType().staticSchema() - Rewrite string IDs.
step.invoke() - Migrate realtime from to v4 native APIs.
@inngest/realtime - Update middleware and logging.
- Configure checkpointing/serverless runtime.
- Typecheck, run tests, and optionally sync with the dev server.
- 更新包版本。
- 修复客户端构造及本地/生产模式配置。
- 将serve选项迁移至客户端。
- 将触发器移入选项。
createFunction - 用/
eventType()替代staticSchema()。EventSchemas - 重写的字符串ID。
step.invoke() - 将实时功能从迁移至v4原生API。
@inngest/realtime - 更新中间件与日志配置。
- 配置检查点/无服务器运行时。
- 类型检查、运行测试,并可选与开发服务器同步。
Package and Environment
包与环境配置
Install the latest v4 SDK:
bash
npm install inngest@latest安装最新v4 SDK:
bash
npm install inngest@latestor pnpm add inngest@latest
或 pnpm add inngest@latest
or yarn add inngest@latest
或 yarn add inngest@latest
If the repo uses v3 realtime, remove `@inngest/realtime`; v4 realtime lives in
the `inngest` package and subpaths such as `inngest/realtime`, `inngest/react`,
and native `step.realtime` / `inngest.realtime`.
v4 defaults to Cloud mode. For local development, use an env var:
```bash
INNGEST_DEV=1 npm run devDo not hardcode in source unless the repo's existing environment
pattern clearly scopes it to local-only code. Production should use
.
isDev: trueINNGEST_SIGNING_KEY
如果仓库使用v3实时功能,移除`@inngest/realtime`;v4实时功能位于`inngest`包及其子路径中,如`inngest/realtime`、`inngest/react`,以及原生的`step.realtime` / `inngest.realtime`。
v4默认使用云模式。本地开发时,使用环境变量:
```bash
INNGEST_DEV=1 npm run dev除非仓库现有环境模式明确限定为仅本地代码,否则不要在源码中硬编码。生产环境应使用。
isDev: trueINNGEST_SIGNING_KEYClient and Serve Options
客户端与Serve选项
In v4, options such as , , and belong
on , not on .
signingKeysigningKeyFallbackbaseUrlnew Inngest(...)serve(...)typescript
// Old v3
app.use(
"/api/inngest",
serve({
client: inngest,
functions,
signingKey: process.env.INNGEST_SIGNING_KEY,
baseUrl: process.env.INNGEST_BASE_URL,
})
);
// New v4
export const inngest = new Inngest({
id: "my-app",
signingKey: process.env.INNGEST_SIGNING_KEY,
baseUrl: process.env.INNGEST_BASE_URL,
});
app.use("/api/inngest", serve({ client: inngest, functions }));If the repo already relies on supported environment variables and does not pass
serve options explicitly, no code change may be required for those keys.
Other renames:
- ->
serveHostserveOrigin - ->
streaming: "force"streaming: true - ->
streaming: "allow"streaming: true - stays
streaming: falsefalse - is removed; pass a
logLevelsuch asloggernew ConsoleLogger({ level })
在v4中,、、等选项应配置在中,而非。
signingKeysigningKeyFallbackbaseUrlnew Inngest(...)serve(...)typescript
// 旧v3写法
app.use(
"/api/inngest",
serve({
client: inngest,
functions,
signingKey: process.env.INNGEST_SIGNING_KEY,
baseUrl: process.env.INNGEST_BASE_URL,
})
);
// 新v4写法
export const inngest = new Inngest({
id: "my-app",
signingKey: process.env.INNGEST_SIGNING_KEY,
baseUrl: process.env.INNGEST_BASE_URL,
});
app.use("/api/inngest", serve({ client: inngest, functions }));如果仓库已依赖支持的环境变量且未显式传递serve选项,则这些配置项无需修改。
其他重命名:
- ->
serveHostserveOrigin - ->
streaming: "force"streaming: true - ->
streaming: "allow"streaming: true - 保持不变
streaming: false - 已移除;需传递
logLevel,如loggernew ConsoleLogger({ level })
createFunction Triggers
createFunction 触发器
Triggers move into the first argument's options object.
typescript
// Old v3
inngest.createFunction(
{ id: "send-welcome" },
{ event: "user/created" },
async ({ event, step }) => {}
);
// New v4
inngest.createFunction(
{ id: "send-welcome", triggers: [{ event: "user/created" }] },
async ({ event, step }) => {}
);Cron triggers move the same way:
typescript
inngest.createFunction(
{ id: "nightly-sync", triggers: [{ cron: "0 2 * * *" }] },
async ({ step }) => {}
);If a function is invoked only via , it may be triggerless.
step.invoke触发器需移入第一个参数的选项对象中。
typescript
// 旧v3写法
inngest.createFunction(
{ id: "send-welcome" },
{ event: "user/created" },
async ({ event, step }) => {}
);
// 新v4写法
inngest.createFunction(
{ id: "send-welcome", triggers: [{ event: "user/created" }] },
async ({ event, step }) => {}
);Cron触发器同样迁移:
typescript
inngest.createFunction(
{ id: "nightly-sync", triggers: [{ cron: "0 2 * * *" }] },
async ({ step }) => {}
);如果函数仅通过调用,则可以无触发器。
step.invokeEventSchemas to eventType/staticSchema
从EventSchemas迁移至eventType/staticSchema
Replace centralized with event-specific definitions.
EventSchemastypescript
import { Inngest, eventType, staticSchema } from "inngest";
import { z } from "zod";
export const userCreated = eventType("user/created", {
schema: z.object({
userId: z.string(),
email: z.string().email(),
}),
});
type InvoicePaid = {
invoiceId: string;
customerId: string;
};
export const invoicePaid = eventType("billing/invoice.paid", {
schema: staticSchema<InvoicePaid>(),
});Use event types consistently:
typescript
await inngest.send(userCreated.create({ userId, email }));
inngest.createFunction(
{ id: "on-user-created", triggers: [userCreated] },
async ({ event }) => {}
);
await step.waitForEvent("wait-for-invoice", {
event: invoicePaid,
timeout: "7d",
});Important: expects a type, not an interface. Convert interfaces
to type aliases when needed.
staticSchema用事件专属定义替换集中式的。
EventSchemastypescript
import { Inngest, eventType, staticSchema } from "inngest";
import { z } from "zod";
export const userCreated = eventType("user/created", {
schema: z.object({
userId: z.string(),
email: z.string().email(),
}),
});
type InvoicePaid = {
invoiceId: string;
customerId: string;
};
export const invoicePaid = eventType("billing/invoice.paid", {
schema: staticSchema<InvoicePaid>(),
});保持事件类型的一致性使用:
typescript
await inngest.send(userCreated.create({ userId, email }));
inngest.createFunction(
{ id: "on-user-created", triggers: [userCreated] },
async ({ event }) => {}
);
await step.waitForEvent("wait-for-invoice", {
event: invoicePaid,
timeout: "7d",
});重要提示:接受类型别名而非接口。必要时将接口转换为类型别名。
staticSchemastep.invoke
step.invoke
v4 no longer accepts raw string function IDs. Use an imported function
reference or .
referenceFunction()typescript
import { referenceFunction } from "inngest";
await step.invoke("run-report", {
function: referenceFunction({
appId: "analytics-app",
functionId: "generate-report",
}),
data: { reportId },
});If the target function is in the same codebase, prefer passing the imported
function itself:
typescript
await step.invoke("run-report", {
function: generateReport,
data: { reportId },
});v4不再接受原始字符串函数ID。需使用导入的函数引用或。
referenceFunction()typescript
import { referenceFunction } from "inngest";
await step.invoke("run-report", {
function: referenceFunction({
appId: "analytics-app",
functionId: "generate-report",
}),
data: { reportId },
});如果目标函数在同一代码库中,优先传递导入的函数本身:
typescript
await step.invoke("run-report", {
function: generateReport,
data: { reportId },
});Realtime Migration
实时功能迁移
v3 realtime used and middleware-injected .
v4 realtime is native.
@inngest/realtimepublishReplace:
- package
@inngest/realtime realtimeMiddleware()- handler args such as
{ publish } - v3 React hooks such as
useInngestSubscription()
With:
- channel definitions from
inngest/realtime - between steps
step.realtime.publish - inside an existing
inngest.realtime.publishstep.run - subscription helpers/hooks from current v4 APIs
Use for detailed patterns. Do not call
from inside ; use
there to avoid step-in-step behavior.
inngest-realtimestep.realtime.publishstep.runinngest.realtime.publishv3实时功能使用和中间件注入的。v4实时功能为原生内置。
@inngest/realtimepublish替换以下内容:
- 包
@inngest/realtime realtimeMiddleware()- 处理器参数如
{ publish } - v3 React钩子如
useInngestSubscription()
替换为:
- 来自的通道定义
inngest/realtime - 步骤间的
step.realtime.publish - 现有内的
step.runinngest.realtime.publish - 当前v4 API中的订阅助手/钩子
详细模式请使用。请勿在内调用;应使用以避免步骤嵌套行为。
inngest-realtimestep.runstep.realtime.publishinngest.realtime.publishParallelism and Checkpointing
并行性与检查点
v4 enables optimized parallelism and checkpointing by default.
Watch for over steps. With optimized parallelism,
waits for all step promises to settle. If the repo relies on first-winner
behavior, use .
Promise.racePromise.racegroup.parallel()For serverless platforms, configure checkpointing slightly below
the platform limit:
maxRuntimetypescript
export const inngest = new Inngest({
id: "my-app",
checkpointing: {
maxRuntime: "50s",
},
});On Vercel or similar frameworks, also set the route handler's max duration
where the platform supports it.
v4默认启用优化的并行性与检查点。
注意步骤上的。在优化并行性下,会等待所有步骤Promise完成。如果仓库依赖先完成即返回的行为,请使用。
Promise.racePromise.racegroup.parallel()对于无服务器平台,配置检查点略低于平台限制:
maxRuntimetypescript
export const inngest = new Inngest({
id: "my-app",
checkpointing: {
maxRuntime: "50s",
},
});在Vercel或类似框架中,还需在平台支持的情况下设置路由处理器的最大时长。
Connect Changes
Connect变更
If the repo uses Connect:
- is replaced by
rewriteGatewayEndpoint.gatewayUrl - Connect may use worker-thread isolation. If integration issues appear, check
or
isolateExecution: false.INNGEST_CONNECT_ISOLATE_EXECUTION=false - Keep target URLs and signing/event keys in env vars.
如果仓库使用Connect:
- 替换为
rewriteGatewayEndpoint。gatewayUrl - Connect可能使用工作线程隔离。如果出现集成问题,检查或
isolateExecution: false。INNGEST_CONNECT_ISOLATE_EXECUTION=false - 将目标URL和签名/事件密钥保留在环境变量中。
Verification
验证环节
Run checks in increasing confidence:
- Package manager install/update.
- Typecheck.
- Unit/integration tests around migrated functions.
- Start the app with .
INNGEST_DEV=1 - Run and confirm function discovery.
npx inngest-cli@latest dev - Send one representative event and inspect the run.
If local dev-server verification is not possible, state exactly which static
checks passed and what runtime verification remains.
按置信度递增顺序运行检查:
- 包管理器安装/更新。
- 类型检查。
- 针对迁移函数的单元/集成测试。
- 使用启动应用。
INNGEST_DEV=1 - 运行并确认函数可被发现。
npx inngest-cli@latest dev - 发送一个代表性事件并检查运行情况。
如果无法进行本地开发服务器验证,请明确说明通过了哪些静态检查以及剩余的运行时验证项。
Common Failure Messages
常见错误信息
- "A signing key is required to run in Cloud mode": set for local development or configure
INNGEST_DEV=1for production.INNGEST_SIGNING_KEY - on
Cls is not a constructor: likely v3/api/inngestmiddleware in a v4 app. Remove the package and migrate to native realtime.@inngest/realtime - fails with string function ID: replace strings with imported function references or
step.invoke.referenceFunction() - Type errors around schemas: replace with
EventSchemasandeventType().staticSchema() - Unexpected behavior: use
Promise.racefor first-winner step races or disable optimized parallelism only when necessary.group.parallel()
- "A signing key is required to run in Cloud mode":本地开发设置,或为生产环境配置
INNGEST_DEV=1。INNGEST_SIGNING_KEY - 在
Cls is not a constructor:可能是v4应用中仍使用v3的/api/inngest中间件。移除该包并迁移至原生实时功能。@inngest/realtime - 使用字符串函数ID失败:将字符串替换为导入的函数引用或
step.invoke。referenceFunction() - 模式相关类型错误:用和
eventType()替换staticSchema()。EventSchemas - 行为不符合预期:对步骤竞争使用
Promise.race实现先完成即返回,或仅在必要时禁用优化并行性。group.parallel()
Anti-Patterns
反模式
- Mixing v3 realtime middleware with .
inngest@4 - Passing ,
signingKey, orbaseUrltosigningKeyFallback.serve() - Moving event trigger syntax but forgetting cron/invoke-triggered functions.
- Replacing with untyped string events everywhere.
EventSchemas - Hardcoding in production-bound source.
isDev: true - Leaving string IDs in .
step.invoke - Skipping typecheck after mechanical migration.
- 在中混合使用v3实时中间件。
inngest@4 - 向传递
serve()、signingKey或baseUrl。signingKeyFallback - 迁移了事件触发器语法,但遗漏了cron/调用触发的函数。
- 将替换为无类型的字符串事件。
EventSchemas - 在生产源码中硬编码。
isDev: true - 中保留字符串ID。
step.invoke - 机械迁移后跳过类型检查。