Marketplace inngest-handler
Create and manage Inngest functions for reliable background jobs, workflows, and scheduled tasks.
git clone https://github.com/aiskillstore/marketplace
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/aayushbaniya2006/inngest-handler" ~/.claude/skills/aiskillstore-marketplace-inngest-handler && rm -rf "$T"
skills/aayushbaniya2006/inngest-handler/SKILL.mdInngest Function Handler Skill
This skill defines the standards for building durable, multi-step workflows using Inngest.
🚨 HARD RULES (Strictly Follow)
-
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.
- ❌ Bad:
-
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.
- Any database write, API call, or non-deterministic logic (random, date) MUST be wrapped in
-
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).
-
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.
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.
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.
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.
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).
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).
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:
in config.{ retries: 10 }
Non-Retriable Errors
Stop execution immediately if the error is fatal (e.g., 400 Bad Request).
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.
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.