inngest-setup
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 for language-specific guidance. Core concepts apply across all languages.
Prerequisites
- Node.js 18+ (Node.js 22.4+ r ecommended 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:
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:
// src/inngest/client.ts
import { Inngest } from "inngest";
export const inngest = new Inngest({
id: "my-app" // Unique identifier for your application (hyphenated slug)
});
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 (preferINNGEST_EVENT_KEYenv var)env: Environment name for Branch EnvironmentsisDev: Force Dev mode (true) or Cloud mode (false)logger: Custom logger instance (e.g. winston, pino) — enablesloggerin function contextmiddleware: Array of middleware (see inngest-middleware skill)schemas: UseEventSchemasfor typed events (see inngest-events skill)
Typed Events with EventSchemas
import { Inngest, EventSchemas } from "inngest";
type Events = {
"user/signup.completed": {
data: {
userId: string;
email: string;
plan: "free" | "pro";
};
};
"order/placed": {
data: {
orderId: string;
amount: number;
};
};
};
export const inngest = new Inngest({
id: "my-app",
schemas: new EventSchemas().fromRecord<Events>()
});
// Now event data is fully typed in functions:
// inngest.createFunction({ id: "handle-signup" }, { event: "user/signup.completed" },
// async ({ event }) => { event.data.userId /* typed as string */ }
// );
Environment Variables Setup
Set these environment variables in your .env file or deployment environment:
# 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.
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:
// 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]
});
// 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]
});
// 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
fastifyPluginfrominngest/fastify - Cloudflare Workers: Use
inngest/cloudflare - AWS Lambda: Use
inngest/lambda - For all other frameworks, check the
servereference here: https://www.inngest.com/docs-markdown/learn/serving-inngest-functions
⚠️ 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:
// src/worker.ts
// Note: inngest/connect requires inngest SDK v3.27+
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_KEYandINNGEST_EVENT_KEYfor production- Set the
appVersionparameter on theInngestclient for production to support rolling deploys
Step 5: Organizing with Apps
As your system grows, organize functions into logical apps:
// 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:
# 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:
{
"sdk-url": [
"http://localhost:3000/api/inngest",
"http://localhost:4000/api/inngest"
],
"port": 8289,
"no-discovery": true
}
Environment-Specific Setup
Local Development
INNGEST_DEV=1
# No keys required in dev mode
Production
INNGEST_EVENT_KEY=evt_your_production_event_key
INNGEST_SIGNING_KEY=signkey_your_production_signing_key
Custom Dev Server Port
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
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
- Create your first Inngest function with
inngest.createFunction() - Test functions using the dev server's "Invoke" button
- Send events with
inngest.send()to trigger functions - Deploy to production with proper environment variables
- See inngest-middleware for adding logging, error tracking, and other cross-cutting concerns
- Monitor functions in the Inngest dashboard
The dev server automatically reloads when you change functions, making development fast and iterative.