← InngestCONTENT HISTORY

Update to Inngest

Snapshot Oct 24, 2026 · 13:41 UTC · version 0.4.1

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save Inngest and check future changes in Following.

Up to 10 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Use when upgrading an existing TypeScript codebase from Inngest SDK v3 to v4, or when fixing mixed v3/v4 API usage. Covers detecting current SDK usage, moving triggers into createFunction options, replacing EventSchemas with eventType/staticSchema, moving serve options to the client, updating realtime imports, rewriting step.invoke string IDs, checkpointing/serverless runtime settings, Connect option changes, and verification.",
  "included_files": [],
  "name": "inngest-v3-v4-migration",
  "skill_md_contents": "---\nname: inngest-v3-v4-migration\ndescription: Use when upgrading an existing TypeScript codebase from Inngest SDK v3 to v4, or when fixing mixed v3/v4 API usage. Covers detecting current SDK usage, moving triggers into createFunction options, replacing EventSchemas with eventType/staticSchema, moving serve options to the client, updating realtime imports, rewriting step.invoke string IDs, checkpointing/serverless runtime settings, Connect option changes, and verification.\n---\n\n# Inngest v3 to v4 Migration\n\nUse this skill when the user asks to upgrade Inngest, fix v3/v4 errors, migrate\nrealtime, or clean up a codebase that has mixed SDK patterns.\n\nPrimary reference:\nhttps://www.inngest.com/docs/reference/typescript/v4/migrations/v3-to-v4\n\nThis skill is agent-first: detect actual usage first, make mechanical API\nchanges in a controlled order, then typecheck and run focused tests.\n\n## When to Trigger\n\nUse this skill for requests like:\n\n- \"Upgrade Inngest from v3 to v4\"\n- \"Fix our Inngest v4 migration\"\n- \"We're getting signing key required / cloud mode errors after upgrading\"\n- \"`step.invoke` with a string function ID stopped working\"\n- \"`@inngest/realtime` broke after installing `inngest@4`\"\n- \"Move from EventSchemas to eventType\"\n- \"Make this existing Inngest repo v4-compatible\"\n\nIf the user asks for a broad codebase reliability audit first, use\n`inngest-brownfield-audit` to choose scope, then return here for the v4 changes.\n\n## Migration Scan\n\nStart by locating all Inngest surfaces:\n\n```bash\nrg -n '\"inngest\"|\"@inngest/realtime\"|\"@inngest/agent-kit\"' package.json **/package.json\nrg -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' .\n```\n\nThen classify the repo:\n\n- **No Inngest yet:** use `inngest-setup`, not this migration skill.\n- **v3 only:** migrate the SDK and all breaking changes together.\n- **mixed v3/v4:** prioritize removing broken v3 APIs from v4 code.\n- **v4 mostly done:** focus on missed runtime gotchas like local dev mode,\n  serverless `maxRuntime`, realtime package imports, and string `step.invoke`.\n\nBefore editing, record:\n\n```text\nInngest migration scan:\n- Current package versions:\n- Client files:\n- Serve/connect entrypoints:\n- Functions using old trigger syntax:\n- EventSchemas usage:\n- Realtime v3 package usage:\n- step.invoke string IDs:\n- Serverless runtime constraints:\n- Tests/checks available:\n```\n\n## Upgrade Order\n\n1. Update package versions.\n2. Fix client construction and local/prod mode.\n3. Move serve options to the client.\n4. Move triggers into `createFunction` options.\n5. Replace `EventSchemas` with `eventType()` / `staticSchema()`.\n6. Rewrite `step.invoke()` string IDs.\n7. Migrate realtime from `@inngest/realtime` to v4 native APIs.\n8. Update middleware and logging.\n9. Configure checkpointing/serverless runtime.\n10. Typecheck, run tests, and optionally sync with the dev server.\n\n## Package and Environment\n\nInstall the latest v4 SDK:\n\n```bash\nnpm install inngest@latest\n# or pnpm add inngest@latest\n# or yarn add inngest@latest\n```\n\nIf the repo uses v3 realtime, remove `@inngest/realtime`; v4 realtime lives in\nthe `inngest` package and subpaths such as `inngest/realtime`, `inngest/react`,\nand native `step.realtime` / `inngest.realtime`.\n\nv4 defaults to Cloud mode. For local development, use an env var:\n\n```bash\nINNGEST_DEV=1 npm run dev\n```\n\nDo not hardcode `isDev: true` in source unless the repo's existing environment\npattern clearly scopes it to local-only code. Production should use\n`INNGEST_SIGNING_KEY`.\n\n## Client and Serve Options\n\nIn v4, options such as `signingKey`, `signingKeyFallback`, and `baseUrl` belong\non `new Inngest(...)`, not on `serve(...)`.\n\n```typescript\n// Old v3\napp.use(\n  \"/api/inngest\",\n  serve({\n    client: inngest,\n    functions,\n    signingKey: process.env.INNGEST_SIGNING_KEY,\n    baseUrl: process.env.INNGEST_BASE_URL,\n  })\n);\n\n// New v4\nexport const inngest = new Inngest({\n  id: \"my-app\",\n  signingKey: process.env.INNGEST_SIGNING_KEY,\n  baseUrl: process.env.INNGEST_BASE_URL,\n});\n\napp.use(\"/api/inngest\", serve({ client: inngest, functions }));\n```\n\nIf the repo already relies on supported environment variables and does not pass\nserve options explicitly, no code change may be required for those keys.\n\nOther renames:\n\n- `serveHost` -> `serveOrigin`\n- `streaming: \"force\"` -> `streaming: true`\n- `streaming: \"allow\"` -> `streaming: true`\n- `streaming: false` stays `false`\n- `logLevel` is removed; pass a `logger` such as `new ConsoleLogger({ level })`\n\n## createFunction Triggers\n\nTriggers move into the first argument's options object.\n\n```typescript\n// Old v3\ninngest.createFunction(\n  { id: \"send-welcome\" },\n  { event: \"user/created\" },\n  async ({ event, step }) => {}\n);\n\n// New v4\ninngest.createFunction(\n  { id: \"send-welcome\", triggers: [{ event: \"user/created\" }] },\n  async ({ event, step }) => {}\n);\n```\n\nCron triggers move the same way:\n\n```typescript\ninngest.createFunction(\n  { id: \"nightly-sync\", triggers: [{ cron: \"0 2 * * *\" }] },\n  async ({ step }) => {}\n);\n```\n\nIf a function is invoked only via `step.invoke`, it may be triggerless.\n\n## EventSchemas to eventType/staticSchema\n\nReplace centralized `EventSchemas` with event-specific definitions.\n\n```typescript\nimport { Inngest, eventType, staticSchema } from \"inngest\";\nimport { z } from \"zod\";\n\nexport const userCreated = eventType(\"user/created\", {\n  schema: z.object({\n    userId: z.string(),\n    email: z.string().email(),\n  }),\n});\n\ntype InvoicePaid = {\n  invoiceId: string;\n  customerId: string;\n};\n\nexport const invoicePaid = eventType(\"billing/invoice.paid\", {\n  schema: staticSchema<InvoicePaid>(),\n});\n```\n\nUse event types consistently:\n\n```typescript\nawait inngest.send(userCreated.create({ userId, email }));\n\ninngest.createFunction(\n  { id: \"on-user-created\", triggers: [userCreated] },\n  async ({ event }) => {}\n);\n\nawait step.waitForEvent(\"wait-for-invoice\", {\n  event: invoicePaid,\n  timeout: \"7d\",\n});\n```\n\nImportant: `staticSchema` expects a type, not an interface. Convert interfaces\nto type aliases when needed.\n\n## step.invoke\n\nv4 no longer accepts raw string function IDs. Use an imported function\nreference or `referenceFunction()`.\n\n```typescript\nimport { referenceFunction } from \"inngest\";\n\nawait step.invoke(\"run-report\", {\n  function: referenceFunction({\n    appId: \"analytics-app\",\n    functionId: \"generate-report\",\n  }),\n  data: { reportId },\n});\n```\n\nIf the target function is in the same codebase, prefer passing the imported\nfunction itself:\n\n```typescript\nawait step.invoke(\"run-report\", {\n  function: generateReport,\n  data: { reportId },\n});\n```\n\n## Realtime Migration\n\nv3 realtime used `@inngest/realtime` and middleware-injected `publish`.\nv4 realtime is native.\n\nReplace:\n\n- `@inngest/realtime` package\n- `realtimeMiddleware()`\n- handler args such as `{ publish }`\n- v3 React hooks such as `useInngestSubscription()`\n\nWith:\n\n- channel definitions from `inngest/realtime`\n- `step.realtime.publish` between steps\n- `inngest.realtime.publish` inside an existing `step.run`\n- subscription helpers/hooks from current v4 APIs\n\nUse `inngest-realtime` for detailed patterns. Do not call\n`step.realtime.publish` from inside `step.run`; use `inngest.realtime.publish`\nthere to avoid step-in-step behavior.\n\n## Parallelism and Checkpointing\n\nv4 enables optimized parallelism and checkpointing by default.\n\nWatch for `Promise.race` over steps. With optimized parallelism, `Promise.race`\nwaits for all step promises to settle. If the repo relies on first-winner\nbehavior, use `group.parallel()`.\n\nFor serverless platforms, configure checkpointing `maxRuntime` slightly below\nthe platform limit:\n\n```typescript\nexport const inngest = new Inngest({\n  id: \"my-app\",\n  checkpointing: {\n    maxRuntime: \"50s\",\n  },\n});\n```\n\nOn Vercel or similar frameworks, also set the route handler's max duration\nwhere the platform supports it.\n\n## Connect Changes\n\nIf the repo uses Connect:\n\n- `rewriteGatewayEndpoint` is replaced by `gatewayUrl`.\n- Connect may use worker-thread isolation. If integration issues appear, check\n  `isolateExecution: false` or `INNGEST_CONNECT_ISOLATE_EXECUTION=false`.\n- Keep target URLs and signing/event keys in env vars.\n\n## Verification\n\nRun checks in increasing confidence:\n\n1. Package manager install/update.\n2. Typecheck.\n3. Unit/integration tests around migrated functions.\n4. Start the app with `INNGEST_DEV=1`.\n5. Run `npx inngest-cli@latest dev` and confirm function discovery.\n6. Send one representative event and inspect the run.\n\nIf local dev-server verification is not possible, state exactly which static\nchecks passed and what runtime verification remains.\n\n## Common Failure Messages\n\n- **\"A signing key is required to run in Cloud mode\"**: set `INNGEST_DEV=1` for\n  local development or configure `INNGEST_SIGNING_KEY` for production.\n- **`Cls is not a constructor` on `/api/inngest`**: likely v3\n  `@inngest/realtime` middleware in a v4 app. Remove the package and migrate to\n  native realtime.\n- **`step.invoke` fails with string function ID**: replace strings with\n  imported function references or `referenceFunction()`.\n- **Type errors around schemas**: replace `EventSchemas` with `eventType()` and\n  `staticSchema()`.\n- **Unexpected `Promise.race` behavior**: use `group.parallel()` for first-winner\n  step races or disable optimized parallelism only when necessary.\n\n## Anti-Patterns\n\n- Mixing v3 realtime middleware with `inngest@4`.\n- Passing `signingKey`, `baseUrl`, or `signingKeyFallback` to `serve()`.\n- Moving event trigger syntax but forgetting cron/invoke-triggered functions.\n- Replacing `EventSchemas` with untyped string events everywhere.\n- Hardcoding `isDev: true` in production-bound source.\n- Leaving string IDs in `step.invoke`.\n- Skipping typecheck after mechanical migration.\n"
}

SHA-256 of public snapshot: e98000f0a0f8f13b19b5547a147bb6cd25f0e8ac0c144873d27e4bc472b3172b