inngest-setup — скилл для Claude Code: Inngest в TypeScript-проекте
Скилл ставит Inngest в TypeScript-проект: клиент, переменные окружения, serve-эндпоинты и локальный dev-сервер. Пригодится, когда нужны фоновые задачи и вебхуки, переживающие падение процесса.
npx skills add inngest/inngest-skills --skill inngest-setup -a claude-codeЧто делает inngest-setup
Скилл проводит агента по шагам установки: пакет inngest, общий файл клиента, переменные окружения, затем выбор режима подключения. Режимов два. Serve-эндпоинт по HTTP подходит для serverless и уже существующих API, а connect через WebSocket — для контейнеров и долгоживущих процессов. Внутри есть готовые примеры для Next.js App Router и Pages Router, Express, Fastify, Cloudflare Workers и AWS Lambda.
Главная ловушка — режим разработки. В v4 клиент по умолчанию работает в Cloud mode, поэтому без INNGEST_DEV=1 эндпоинт отвечает 500 с сообщением про отсутствующий signing key, а dev-сервер не синхронизируется с приложением. Скилл советует задавать переменную в .env или в скрипте package.json и не прописывать isDev: true прямо в коде — в продакшене это тихо ломается. Для Cloud mode нужен INNGEST_SIGNING_KEY, для отправки событий — INNGEST_EVENT_KEY.
Ещё в v4 часть настроек переехала: signingKey, signingKeyFallback и baseUrl теперь задаются в конструкторе Inngest, а не в serve(). Сам serve() принимает только client, functions и streaming. Для Express нужен express.json({ limit: "10mb" }), иначе крупное состояние функции не пройдёт. Путь эндпоинта лучше оставить /api/inngest — по нему работает автообнаружение. Скилл рассчитан на TypeScript; для Python и Go он отправляет в документацию Inngest. Нужны Node.js 18+, TypeScript-проект и пакетный менеджер.
Какие задачи решает
- поставить Inngest в TypeScript-проект с нуля
- сделать вебхук, который безопасно повторяется при сбое
- запустить фоновую задачу, переживающую падение процесса
- настроить расписание и длинные воркфлоу вне одного запроса
- подключить serve-эндпоинт для Next.js, Express, Hono или Fastify
Примеры запросов агенту
- Поставь Inngest в этот проект: клиент, переменные окружения и serve-эндпоинт для Next.js App Router
- Настрой connect-as-worker режим для контейнера и добавь функцию обработки события
order/placed - Почему GET
/api/inngestвозвращает 500 — проверь режим разработки и signing key
Как установить inngest-setup
1Через Skills CLI (Vercel)
npx skills add inngest/inngest-skills --skill inngest-setup -a claude-code2Вручную: положить файлы
~/.claude/skills/inngest-setup/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/inngest-setup/
1Через Skills CLI (Vercel)
npx skills add inngest/inngest-skills --skill inngest-setup -a codex2Вручную: положить файлы
.agents/skills/inngest-setup/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add inngest/inngest-skills --skill inngest-setup -a cursor2Вручную: положить файлы
.cursor/skills/inngest-setup/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install inngest/inngest-skills inngest-setup2Вручную: положить файлы
.github/skills/inngest-setup/SKILL.md1Через Skills CLI (Vercel)
npx skills add inngest/inngest-skills --skill inngest-setup -a gemini-cli2Командой платформы
gemini skills install https://github.com/inngest/inngest-skills --path skills/inngest-setupВручную: положить файлы
.devin/skills/inngest-setup/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add inngest/inngest-skills --skill inngest-setup -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о inngest-setup
Как установить inngest-setup в Claude?
Выполните npx skills add inngest/inngest-skills --skill inngest-setup -a claude-code. После этого файл скилла лежит в ~/.claude/skills/inngest-setup/SKILL.md.
Что делает inngest-setup?
Проводит по установке Inngest в TypeScript-проект: SDK, файл клиента, переменные окружения, serve-эндпоинты для разных фреймворков, режим connect и локальный dev-сервер.
Почему эндпоинт возвращает 500 в локальной разработке?
В v4 клиент по умолчанию работает в Cloud mode. Задайте INNGEST_DEV=1 в .env или в dev-скрипте, иначе сервер сообщит об отсутствии signing key.
Исходный текст
SKILL.md · Apache-2.0
---
name: inngest-setup
description: Use 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, serve endpoints (Next.js, Express, Hono, Fastify), connect-as-worker mode, and the local dev server.
---
# Inngest Setup
This skill sets up Inngest in a TypeScript project from scratch, covering installation, client configuration, connection modes, and local development.
> **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.
## Prerequisites
- Node.js 18+ (Node.js 22.4+ recommended for WebSocket support)
- TypeScript project
- Package manager (npm, yarn, pnpm, or bun)
## Step 1: Install the Inngest SDK
Install the `inngest` npm package in your project:
```bash
npm install inngest
# or
yarn add inngest
# or
pnpm add inngest
# or
bun add inngest
```
## Step 2: Create an Inngest Client
Create a shared client file that you'll import throughout your codebase:
```typescript
// src/inngest/client.ts
import { Inngest } from "inngest";
export const inngest = new Inngest({
id: "my-app" // Unique identifier for your application (hyphenated slug)
});
// IMPORTANT: v4 defaults to Cloud mode. For local dev, set INNGEST_DEV=1 env var.
// Without it, your serve endpoint will return 500 ("In cloud mode but no signing key").
// In production, set INNGEST_SIGNING_KEY (required for Cloud mode).
```
### Key Configuration Options
- **`id`** (required): Unique identifier for your app. Use a hyphenated slug like `"my-app"` or `"user-service"`
- **`eventKey`**: Event key for sending events (prefer `INNGEST_EVENT_KEY` env var)
- **`env`**: Environment name for Branch Environments
- **`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.
- **`signingKey`**: Signing key for production (prefer `INNGEST_SIGNING_KEY` env var). Moved from `serve()` to client in v4
- **`signingKeyFallback`**: Fallback signing key for key rotation (prefer `INNGEST_SIGNING_KEY_FALLBACK` env var)
- **`baseUrl`**: Custom Inngest API base URL (prefer `INNGEST_BASE_URL` env var)
- **`logger`**: Custom logger instance (e.g. winston, pino) — enables `logger` in function context
- **`middleware`**: Array of middleware (see **inngest-middleware** skill)
### Typed Events with eventType()
```typescript
import { Inngest, eventType } from "inngest";
import { z } from "zod";
const signupCompleted = eventType("user/signup.completed", {
schema: z.object({
userId: z.string(),
email: z.string(),
plan: z.enum(["free", "pro"])
})
});
const orderPlaced = eventType("order/placed", {
schema: z.object({
orderId: z.string(),
amount: z.number()
})
});
export const inngest = new Inngest({ id: "my-app" });
// Use event types as triggers for full type safety:
inngest.createFunction(
{ id: "handle-signup", triggers: [signupCompleted] },
async ({ event }) => {
event.data.userId; /* typed as string */
}
);
// Use event types when sending events:
await inngest.send(
signupCompleted.create({
userId: "user_123",
email: "[email protected]",
plan: "pro"
})
);
```
### Environment Variables Setup
Set these environment variables in your `.env` file or deployment environment:
```env
# Required for production
INNGEST_EVENT_KEY=your-event-key-here
INNGEST_SIGNING_KEY=your-signing-key-here
# Force dev mode during local development
INNGEST_DEV=1
# Optional - custom dev server URL (default: http://localhost:8288)
INNGEST_BASE_URL=http://localhost:8288
```
**⚠️ Common Gotcha**: Never hardcode keys in your source code. Always use environment variables for `INNGEST_EVENT_KEY` and `INNGEST_SIGNING_KEY`.
## CRITICAL: Enable Dev Mode for Local Development
**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.
Add to your `.env` file (or your dev script in package.json):
```env
INNGEST_DEV=1
```
Or in `package.json` scripts:
```json
{
"scripts": {
"dev": "INNGEST_DEV=1 tsx --watch src/server.ts"
}
}
```
**Symptoms of missing INNGEST_DEV:**
- GET `/api/inngest` returns `{"code":"internal_server_error"}`
- Server logs: "In cloud mode but no signing key found"
- Dev server can't sync with your app
## Step 3: Choose Your Connection Mode
Inngest supports two connection modes:
### Mode A: Serve Endpoint (HTTP)
Best for serverless platforms (Vercel, Lambda, etc.) and existing APIs.
### Mode B: Connect (WebSocket)
Best for container runtimes (Kubernetes, Docker) and long-running processes.
## Step 4A: Serving an Endpoint (HTTP Mode)
Create an API endpoint that exposes your functions to Inngest:
```typescript
// For Next.js App Router: src/app/api/inngest/route.ts
import { serve } from "inngest/next";
import { inngest } from "../../../inngest/client";
import { myFunction } from "../../../inngest/functions";
export const { GET, POST, PUT } = serve({
client: inngest,
functions: [myFunction]
});
```
```typescript
// For Next.js Pages Router: pages/api/inngest.ts
import { serve } from "inngest/next";
import { inngest } from "../../inngest/client";
import { myFunction } from "../../inngest/functions";
export default serve({
client: inngest,
functions: [myFunction]
});
```
```typescript
// For Express.js
import express from "express";
import { serve } from "inngest/express";
import { inngest } from "./inngest/client";
import { myFunction } from "./inngest/functions";
const app = express();
app.use(express.json({ limit: "10mb" })); // Required for Inngest, increase limit for larger function state
app.use(
"/api/inngest",
serve({
client: inngest,
functions: [myFunction]
})
);
```
**🔧 Framework-Specific Notes**:
- **Express**: Must use `express.json({ limit: "10mb" })` middleware to support larger function state.
- **Fastify**: Use `fastifyPlugin` from `inngest/fastify`
- **Cloudflare Workers**: Use `inngest/cloudflare`
- **AWS Lambda**: Use `inngest/lambda`
- For all other frameworks, check the `serve` reference here: https://www.inngest.com/docs-markdown/learn/serving-inngest-functions
**⚠️ v4 Change:** Options like `signingKey`, `signingKeyFallback`, and `baseUrl` are now configured on the `Inngest` client constructor, not on `serve()`. The `serve()` function only accepts `client`, `functions`, and `streaming`.
**⚠️ Common Gotcha**: Always use `/api/inngest` as your endpoint path. This enables automatic discovery. If you must use a different path, you'll need to configure discovery manually with the `-u` flag.
## Step 4B: Connect as Worker (WebSocket Mode)
For long-running applications that maintain persistent connections:
```typescript
// src/worker.ts
import { connect } from "inngest/connect";
import { inngest } from "./inngest/client";
import { myFunction } from "./inngest/functions";
(async () => {
const connection = await connect({
apps: [{ client: inngest, functions: [myFunction] }],
instanceId: process.env.HOSTNAME, // Unique worker identifier
maxWorkerConcurrency: 10 // Max concurrent steps
});
console.log("Worker connected:", connection.state);
// Graceful shutdown handling
await connection.closed;
console.log("Worker shut down");
})();
```
**Requirements for Connect Mode**:
- Node.js 22.4+ (or Deno 1.4+, Bun 1.1+) for WebSocket support
- Long-running server environment (not serverless)
- `INNGEST_SIGNING_KEY` and `INNGEST_EVENT_KEY` for production
- Set the `appVersion` parameter on the `Inngest` client for production to support rolling deploys
**v4 Connect Changes:**
- **Worker thread isolation** is enabled by default — WebSocket connections execute in a worker thread to prevent event loop starvation. Set `isolateExecution: false` to use a single process (or `INNGEST_CONNECT_ISOLATE_EXECUTION=false`)
- **`rewriteGatewayEndpoint`** callback has been replaced with the `gatewayUrl` string option (or `INNGEST_CONNECT_GATEWAY_URL` env var)
## Step 5: Organizing with Apps
As your system grows, organize functions into logical apps:
```typescript
// User service
const userService = new Inngest({ id: "user-service" });
// Payment service
const paymentService = new Inngest({ id: "payment-service" });
// Email service
const emailService = new Inngest({ id: "email-service" });
```
Each app gets its own section in the Inngest dashboard and can be deployed independently. Use descriptive, hyphenated IDs that match your service architecture.
**⚠️ Common Gotcha**: Changing an app's `id` creates a new app in Inngest. Keep IDs consistent across deployments.
## Step 6: Local Development with inngest-cli
Start the Inngest Dev Server for local development:
```bash
# Auto-discover your app on common ports/endpoints
npx --ignore-scripts=false inngest-cli@latest dev
# Specify your app's URL manually
npx --ignore-scripts=false inngest-cli@latest dev -u http://localhost:3000/api/inngest
# Custom port for dev server
npx --ignore-scripts=false inngest-cli@latest dev -p 9999
# Disable auto-discovery
npx --ignore-scripts=false inngest-cli@latest dev --no-discovery -u http://localhost:3000/api/inngest
# Multiple apps
npx --ignore-scripts=false inngest-cli@latest dev -u http://localhost:3000/api/inngest -u http://localhost:4000/api/inngest
```
The dev server will be available at `http://localhost:8288` by default.
### Configuration File (Optional)
Create `inngest.json` for complex setups:
```json
{
"sdk-url": [
"http://localhost:3000/api/inngest",
"http://localhost:4000/api/inngest"
],
"port": 8289,
"no-discovery": true
}
```
## Environment-Specific Setup
### Local Development
```env
INNGEST_DEV=1
# No keys required in dev mode
```
### Production
```env
INNGEST_EVENT_KEY=evt_your_production_event_key
INNGEST_SIGNING_KEY=signkey_your_production_signing_key
```
### Custom Dev Server Port
```env
INNGEST_DEV=1
INNGEST_BASE_URL=http://localhost:9999
```
If your app runs on a non-standard port (not 3000), make sure the dev server can reach it by specifying the URL with `-u` flag.
## Common Issues & Solutions
**Port Conflicts**: If port 8288 is in use, specify a different port: `-p 9999`
**Auto-discovery Not Working**: Use manual URL specification: `-u http://localhost:YOUR_PORT/api/inngest`. If using `--no-discovery` flag, the `-u` flag is **required** — the dev server will not find your app without it.
**Functions Not Showing in Dev Server**: Your app must register with the dev server. This happens automatically when your serve endpoint receives its first request from the dev server. If registration isn't happening: (1) verify `INNGEST_DEV=1` is set, (2) verify the dev server can reach your app URL, (3) try restarting your app while the dev server is running.
**Signature Verification Errors**: Ensure `INNGEST_SIGNING_KEY` is set correctly in production
**WebSocket Connection Issues**: Verify Node.js version 22.4+ for connect mode
**Docker Development**: Use `host.docker.internal` for app URLs when running dev server in Docker
## Next Steps
1. Create your first Inngest function with `inngest.createFunction()`
2. Test functions using the dev server's "Invoke" button
3. Send events with `inngest.send()` to trigger functions
4. Deploy to production with proper environment variables
5. See **inngest-middleware** for adding logging, error tracking, and other cross-cutting concerns
6. Monitor functions in the Inngest dashboard
The dev server automatically reloads when you change functions, making development fast and iterative.
© inngest · Apache-2.0 · inngest/inngest-skills