$npx -y skills add inngest/inngest-skills --skill inngest-setupUse when adding durable execution to a TypeScript project — building retry-safe webhook handlers, background jobs that survive crashes, scheduled tasks, or long-running workflows that outlive a single request. Covers Inngest SDK installation, client config, environment variables,
| 1 | # Inngest Setup |
| 2 | |
| 3 | This skill sets up Inngest in a TypeScript project from scratch, covering installation, client configuration, connection modes, and local development. |
| 4 | |
| 5 | > **These skills are focused on TypeScript.** For Python or Go, refer to the [Inngest documentation](https://www.inngest.com/llms.txt) for language-specific guidance. Core concepts apply across all languages. |
| 6 | |
| 7 | ## Prerequisites |
| 8 | |
| 9 | - Node.js 18+ (Node.js 22.4+ recommended for WebSocket support) |
| 10 | - TypeScript project |
| 11 | - Package manager (npm, yarn, pnpm, or bun) |
| 12 | |
| 13 | ## Step 1: Install the Inngest SDK |
| 14 | |
| 15 | Install the `inngest` npm package in your project: |
| 16 | |
| 17 | ```bash |
| 18 | npm install inngest |
| 19 | # or |
| 20 | yarn add inngest |
| 21 | # or |
| 22 | pnpm add inngest |
| 23 | # or |
| 24 | bun add inngest |
| 25 | ``` |
| 26 | |
| 27 | ## Step 2: Create an Inngest Client |
| 28 | |
| 29 | Create a shared client file that you'll import throughout your codebase: |
| 30 | |
| 31 | ```typescript |
| 32 | // src/inngest/client.ts |
| 33 | import { Inngest } from "inngest"; |
| 34 | |
| 35 | export const inngest = new Inngest({ |
| 36 | id: "my-app" // Unique identifier for your application (hyphenated slug) |
| 37 | }); |
| 38 | // IMPORTANT: v4 defaults to Cloud mode. For local dev, set INNGEST_DEV=1 env var. |
| 39 | // Without it, your serve endpoint will return 500 ("In cloud mode but no signing key"). |
| 40 | // In production, set INNGEST_SIGNING_KEY (required for Cloud mode). |
| 41 | ``` |
| 42 | |
| 43 | ### Key Configuration Options |
| 44 | |
| 45 | - **`id`** (required): Unique identifier for your app. Use a hyphenated slug like `"my-app"` or `"user-service"` |
| 46 | - **`eventKey`**: Event key for sending events (prefer `INNGEST_EVENT_KEY` env var) |
| 47 | - **`env`**: Environment name for Branch Environments |
| 48 | - **`isDev`**: Force Dev mode (`true`) or Cloud mode (`false`). **v4 defaults to Cloud mode**, so set `INNGEST_DEV=1` env var for local development. **Never hardcode `isDev: true` in source code** — it will silently break in production. Always use the env var. |
| 49 | - **`signingKey`**: Signing key for production (prefer `INNGEST_SIGNING_KEY` env var). Moved from `serve()` to client in v4 |
| 50 | - **`signingKeyFallback`**: Fallback signing key for key rotation (prefer `INNGEST_SIGNING_KEY_FALLBACK` env var) |
| 51 | - **`baseUrl`**: Custom Inngest API base URL (prefer `INNGEST_BASE_URL` env var) |
| 52 | - **`logger`**: Custom logger instance (e.g. winston, pino) — enables `logger` in function context |
| 53 | - **`middleware`**: Array of middleware (see **inngest-middleware** skill) |
| 54 | |
| 55 | ### Typed Events with eventType() |
| 56 | |
| 57 | ```typescript |
| 58 | import { Inngest, eventType } from "inngest"; |
| 59 | import { z } from "zod"; |
| 60 | |
| 61 | const signupCompleted = eventType("user/signup.completed", { |
| 62 | schema: z.object({ |
| 63 | userId: z.string(), |
| 64 | email: z.string(), |
| 65 | plan: z.enum(["free", "pro"]) |
| 66 | }) |
| 67 | }); |
| 68 | |
| 69 | const orderPlaced = eventType("order/placed", { |
| 70 | schema: z.object({ |
| 71 | orderId: z.string(), |
| 72 | amount: z.number() |
| 73 | }) |
| 74 | }); |
| 75 | |
| 76 | export const inngest = new Inngest({ id: "my-app" }); |
| 77 | |
| 78 | // Use event types as triggers for full type safety: |
| 79 | inngest.createFunction( |
| 80 | { id: "handle-signup", triggers: [signupCompleted] }, |
| 81 | async ({ event }) => { |
| 82 | event.data.userId; /* typed as string */ |
| 83 | } |
| 84 | ); |
| 85 | |
| 86 | // Use event types when sending events: |
| 87 | await inngest.send( |
| 88 | signupCompleted.create({ |
| 89 | userId: "user_123", |
| 90 | email: "user@example.com", |
| 91 | plan: "pro" |
| 92 | }) |
| 93 | ); |
| 94 | ``` |
| 95 | |
| 96 | ### Environment Variables Setup |
| 97 | |
| 98 | Set these environment variables in your `.env` file or deployment environment: |
| 99 | |
| 100 | ```env |
| 101 | # Required for production |
| 102 | INNGEST_EVENT_KEY=your-event-key-here |
| 103 | INNGEST_SIGNING_KEY=your-signing-key-here |
| 104 | |
| 105 | # Force dev mode during local development |
| 106 | INNGEST_DEV=1 |
| 107 | |
| 108 | # Optional - custom dev server URL (default: http://localhost:8288) |
| 109 | INNGEST_BASE_URL=http://localhost:8288 |
| 110 | ``` |
| 111 | |
| 112 | **⚠️ Common Gotcha**: Never hardcode keys in your source code. Always use environment variables for `INNGEST_EVENT_KEY` and `INNGEST_SIGNING_KEY`. |
| 113 | |
| 114 | ## CRITICAL: Enable Dev Mode for Local Development |
| 115 | |
| 116 | **Before creating serve endpoints or connecting workers, ensure dev mode is enabled.** Without it, Inngest defaults to Cloud mode and your endpoints will fail with 500 errors. |
| 117 | |
| 118 | Add to your `.env` file (or your dev script in package.json): |
| 119 | |
| 120 | ```env |
| 121 | INNGEST_DEV=1 |
| 122 | ``` |
| 123 | |
| 124 | Or in `package.json` scripts: |
| 125 | |
| 126 | ```json |
| 127 | { |
| 128 | "scripts": { |
| 129 | "dev": "INNGEST_DEV=1 tsx --watch src/server.ts" |
| 130 | } |
| 131 | } |
| 132 | ``` |
| 133 | |
| 134 | **Symptoms of missing INNGEST_DEV:** |
| 135 | - GET `/api/inngest` returns `{"code":"internal_server_error"}` |
| 136 | - Server logs: "In cloud mode but no signing key found" |
| 137 | - Dev server can't sync with your app |
| 138 | |
| 139 | ## Step 3: Choose Your Connection Mode |
| 140 | |
| 141 | Inngest supports two connection modes: |
| 142 | |
| 143 | ### Mode A: Serve Endpoint (HTTP) |
| 144 | |
| 145 | Best for serverless platforms (Vercel, Lambda, etc.) and existing APIs. |
| 146 | |
| 147 | ### Mode B: Connect (WebSocket) |
| 148 | |
| 149 | Best for container ru |