Why Use This
This skill provides specialized capabilities for aiskillstore's codebase.
Use Cases
- Developing new features in the aiskillstore repository
- Refactoring existing code to follow aiskillstore standards
- Understanding and working with aiskillstore's codebase structure
Install Guide
2 steps - 1
- 2
Install inside Ananke
Click Install Skill, paste the link below, then press Install.
https://github.com/aiskillstore/marketplace/tree/main/skills/aayushbaniya2006/inngest-handler
Skill Snapshot
Auto scan of skill assets. Informational only.
Valid SKILL.md
Checks against SKILL.md specification
Source & Community
Updated At Jan 19, 2026, 04:39 AM
Skill Stats
SKILL.md 164 Lines
Total Files 1
Total Size 0 B
License NOASSERTION
---
name: inngest-handler
description: Create and manage Inngest functions for reliable background jobs, workflows, and scheduled tasks.
---
# Inngest Function Handler Skill
This skill defines the standards for building durable, multi-step workflows using Inngest.
## 🚨 HARD RULES (Strictly Follow)
1. **NO `setTimeout` / `setInterval`**:
- ❌ **Bad**: `await new Promise(r => setTimeout(r, 1000))`
- ✅ **Good**: `await step.sleep("wait-1s", "1s")`
- *Reason*: Serverless functions time out; Inngest sleeps persist for up to a year.
2. **NO Side Effects Outside Steps**:
- Any database write, API call, or non-deterministic logic (random, date) **MUST** be wrapped in `step.run()`.
- *Reason*: Inngest functions execute multiple times (memoization). Code outside steps runs every time.
3. **Deterministic Steps**:
- Steps are memoized by their ID (1st arg). IDs must be unique and stable.
- Do not dynamically generate step IDs unless you know what you are doing (e.g., inside loops with index).
4. **Return Data from Steps**:
- If you need a value later, return it from the step.
- ❌ **Bad**: `let userId; await step.run(..., () => { userId = ... })`
- ✅ **Good**: `const userId = await step.run(..., () => { return ... })`
## Core Patterns
### 1. Multi-Step Execution
Wrap all logic in steps to ensure retriability and resumability.
```typescript
export const processOrder = inngest.createFunction(
{ id: "process-order" },
{ event: "shop/order.created" },
async ({ event, step }) => {
// 1. Step: Validate (Retriable)
const user = await step.run("get-user", async () => {
return await db.users.findById(event.data.userId);
});
// 2. Step: Sleep (Durable pause)
await step.sleep("wait-for-payment", "1h");
// 3. Step: Wait for Event (Human/System interaction)
const payment = await step.waitForEvent("wait-payment", {
event: "shop/payment.success",
match: "data.orderId",
timeout: "24h"
});
// 4. Step: Conditional Logic
if (!payment) {
await step.run("cancel-order", async () => { ... });
}
}
);
```
### 2. Parallelism
Run steps concurrently to speed up execution.
```typescript
const [user, subscription] = await Promise.all([
step.run("fetch-user", () => db.users.find(...)),
step.run("fetch-sub", () => stripe.subscriptions.retrieve(...))
]);
```
### 3. Working with Loops
Inside loops, ensure step IDs are unique.
```typescript
const items = event.data.items;
for (const item of items) {
// Use dynamic ID to ensure uniqueness per item
await step.run(`process-item-${item.id}`, async () => {
await processItem(item);
});
}
```
## Configuration & Flow Control
### Rate Limiting & Throttling
Prevent overwhelming 3rd party APIs.
```typescript
inngest.createFunction({
id: "sync-crm",
// Max 10 requests per minute per user
rateLimit: { limit: 10, period: "1m", key: "event.data.userId" },
// Drop events if queue is full
throttle: { limit: 5, period: "1s" }
}, ...);
```
### Debounce
Process only the latest event in a window (e.g., search indexing).
```typescript
inngest.createFunction({
id: "index-product",
// Wait 10s for more events; only run with the latest data
debounce: { period: "10s", key: "event.data.productId" }
}, ...);
```
### Priority
Prioritize specific events (e.g., Paid users).
```typescript
inngest.createFunction({
id: "generate-report",
// High number = High priority
priority: { run: "event.data.plan === 'enterprise' ? 100 : 0" }
}, ...);
```
## Error Handling
### Automatic Retries
Inngest retries steps automatically on error (default ~4-5 times with backoff).
- **Customize**: `{ retries: 10 }` in config.
### Non-Retriable Errors
Stop execution immediately if the error is fatal (e.g., 400 Bad Request).
```typescript
import { NonRetriableError } from "inngest";
await step.run("validate", async () => {
if (!isValid) throw new NonRetriableError("Invalid payload");
});
```
### Failure Handlers (Rollbacks)
Execute cleanup logic if the function fails after all retries.
```typescript
export const riskyFunc = inngest.createFunction(
{
id: "risky-transfer",
// Runs if main handler fails
onFailure: async ({ error, event, step }) => {
await step.run("rollback-funds", async () => {
await reverseTransfer(event.data.transferId);
});
await step.run("notify-admin", async () => {
await sendAlert(`Transfer failed: ${error.message}`);
});
}
},
{ event: "bank/transfer.init" },
async ({ step }) => { /* ... */ }
);
```
## Registration
**MANDATORY**: All functions must be imported and exported in `src/lib/inngest/functions/index.ts`.