[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-fc5ed895-ffac-4027-94dc-d51bb27a78bb":3,"$fUcUnNZVtvuJD1gpmdejyn1xWa5e_rzyVeUkE8HZD4yk":43},{"id":4,"title":5,"description":6,"categoryId":7,"moduleId":8,"tags":9,"prompt":10,"icon":11,"source":12,"sourceUrl":13,"authorId":14,"authorName":15,"isPublic":16,"stars":17,"runs":18,"createdAt":19,"updatedAt":19,"module":20,"category":27,"packages":34},"fc5ed895-ffac-4027-94dc-d51bb27a78bb","inngest","无服务器背景作业、事件驱动专家","cat_life_career","mod_other","sickn33,other","---\nname: inngest\ndescription: Inngest expert for serverless-first background jobs, event-driven\n  workflows, and durable execution without managing queues or workers.\nrisk: none\nsource: vibeship-spawner-skills (Apache 2.0)\ndate_added: 2026-02-27\n---\n\n# Inngest Integration\n\nInngest expert for serverless-first background jobs, event-driven workflows,\nand durable execution without managing queues or workers.\n\n## Principles\n\n- Events are the primitive - everything triggers from events, not queues\n- Steps are your checkpoints - each step result is durably stored\n- Sleep is not a hack - Inngest sleeps are real, not blocking threads\n- Retries are automatic - but you control the policy\n- Functions are just HTTP handlers - deploy anywhere that serves HTTP\n- Concurrency is a first-class concern - protect downstream services\n- Idempotency keys prevent duplicates - use them for critical operations\n- Fan-out is built-in - one event can trigger many functions\n\n## Capabilities\n\n- inngest-functions\n- event-driven-workflows\n- step-functions\n- serverless-background-jobs\n- durable-sleep\n- fan-out-patterns\n- concurrency-control\n- scheduled-functions\n\n## Scope\n\n- redis-queues -> bullmq-specialist\n- workflow-orchestration -> temporal-craftsman\n- message-streaming -> event-architect\n- infrastructure -> infra-architect\n\n## Tooling\n\n### Core\n\n- inngest\n- inngest-cli\n\n### Frameworks\n\n- nextjs\n- express\n- hono\n- remix\n- sveltekit\n\n### Deployment\n\n- vercel\n- cloudflare-workers\n- netlify\n- railway\n- fly-io\n\n### Patterns\n\n- step-functions\n- event-fan-out\n- scheduled-cron\n- webhook-handling\n\n## Patterns\n\n### Basic Function Setup\n\nInngest function with typed events in Next.js\n\n**When to use**: Starting with Inngest in any Next.js project\n\n\u002F\u002F lib\u002Finngest\u002Fclient.ts\nimport { Inngest } from 'inngest';\n\nexport const inngest = new Inngest({\n  id: 'my-app',\n  schemas: new EventSchemas().fromRecord\u003CEvents>(),\n});\n\n\u002F\u002F Define your events with types\ntype Events = {\n  'user\u002Fsigned.up': { data: { userId: string; email: string } };\n  'order\u002Fplaced': { data: { orderId: string; total: number } };\n};\n\n\u002F\u002F lib\u002Finngest\u002Ffunctions.ts\nimport { inngest } from '.\u002Fclient';\n\nexport const sendWelcomeEmail = inngest.createFunction(\n  { id: 'send-welcome-email' },\n  { event: 'user\u002Fsigned.up' },\n  async ({ event, step }) => {\n    \u002F\u002F Step 1: Get user details\n    const user = await step.run('get-user', async () => {\n      return await db.users.findUnique({ where: { id: event.data.userId } });\n    });\n\n    \u002F\u002F Step 2: Send welcome email\n    await step.run('send-email', async () => {\n      await resend.emails.send({\n        to: user.email,\n        subject: 'Welcome!',\n        template: 'welcome',\n      });\n    });\n\n    \u002F\u002F Step 3: Wait 24 hours, then send tips\n    await step.sleep('wait-for-tips', '24h');\n\n    await step.run('send-tips', async () => {\n      await resend.emails.send({\n        to: user.email,\n        subject: 'Getting Started Tips',\n        template: 'tips',\n      });\n    });\n  }\n);\n\n\u002F\u002F app\u002Fapi\u002Finngest\u002Froute.ts (Next.js App Router)\nimport { serve } from 'inngest\u002Fnext';\nimport { inngest } from '@\u002Flib\u002Finngest\u002Fclient';\nimport { sendWelcomeEmail } from '@\u002Flib\u002Finngest\u002Ffunctions';\n\nexport const { GET, POST, PUT } = serve({\n  client: inngest,\n  functions: [sendWelcomeEmail],\n});\n\n### Multi-Step Workflow\n\nComplex workflow with parallel steps and error handling\n\n**When to use**: Processing that involves multiple services or long waits\n\nexport const processOrder = inngest.createFunction(\n  {\n    id: 'process-order',\n    retries: 3,\n    concurrency: { limit: 10 },  \u002F\u002F Max 10 orders processing at once\n  },\n  { event: 'order\u002Fplaced' },\n  async ({ event, step }) => {\n    const { orderId } = event.data;\n\n    \u002F\u002F Parallel steps - both run simultaneously\n    const [inventory, payment] = await Promise.all([\n      step.run('check-inventory', () => checkInventory(orderId)),\n      step.run('validate-payment', () => validatePayment(orderId)),\n    ]);\n\n    if (!inventory.available) {\n      \u002F\u002F Send event instead of direct call (fan-out pattern)\n      await step.sendEvent('notify-backorder', {\n        name: 'order\u002Fbackordered',\n        data: { orderId, items: inventory.missing },\n      });\n      return { status: 'backordered' };\n    }\n\n    \u002F\u002F Process payment\n    const charge = await step.run('charge-payment', async () => {\n      return await stripe.charges.create({\n        amount: event.data.total,\n        customer: payment.customerId,\n      });\n    });\n\n    \u002F\u002F Ship order\n    await step.run('ship-order', () => fulfillment.ship(orderId));\n\n    return { status: 'completed', chargeId: charge.id };\n  }\n);\n\n### Scheduled\u002FCron Functions\n\nFunctions that run on a schedule\n\n**When to use**: Recurring tasks like daily reports or cleanup jobs\n\nexport const dailyDigest = inngest.createFunction(\n  { id: 'daily-digest' },\n  { cron: '0 9 * * *' },  \u002F\u002F Every day at 9am UTC\n  async ({ step }) => {\n    \u002F\u002F Get all users who want digests\n    const users = await step.run('get-users', async () => {\n      return await db.users.findMany({\n        where: { digestEnabled: true },\n      });\n    });\n\n    \u002F\u002F Send to each user (creates child events)\n    await step.sendEvent(\n      'send-digests',\n      users.map(user => ({\n        name: 'digest\u002Fsend',\n        data: { userId: user.id },\n      }))\n    );\n\n    return { sent: users.length };\n  }\n);\n\n\u002F\u002F Separate function handles individual digest sending\nexport const sendDigest = inngest.createFunction(\n  { id: 'send-digest', concurrency: { limit: 50 } },\n  { event: 'digest\u002Fsend' },\n  async ({ event, step }) => {\n    \u002F\u002F ... send individual digest\n  }\n);\n\n### Webhook Handler with Idempotency\n\nSafely process webhooks with deduplication\n\n**When to use**: Handling Stripe, GitHub, or other webhooks\n\nexport const handleStripeWebhook = inngest.createFunction(\n  {\n    id: 'stripe-webhook',\n    \u002F\u002F Deduplicate by Stripe event ID\n    idempotency: 'event.data.stripeEventId',\n  },\n  { event: 'stripe\u002Fwebhook.received' },\n  async ({ event, step }) => {\n    const { type, data } = event.data;\n\n    switch (type) {\n      case 'checkout.session.completed':\n        await step.run('fulfill-order', async () => {\n          await fulfillOrder(data.session.id);\n        });\n        break;\n\n      case 'customer.subscription.deleted':\n        await step.run('cancel-subscription', async () => {\n          await cancelSubscription(data.subscription.id);\n        });\n        break;\n    }\n  }\n);\n\n### AI Pipeline with Long Processing\n\nMulti-step AI processing with chunked work\n\n**When to use**: AI workflows that may take minutes to complete\n\nexport const processDocument = inngest.createFunction(\n  {\n    id: 'process-document',\n    retries: 2,\n    concurrency: { limit: 5 },  \u002F\u002F Limit API usage\n  },\n  { event: 'document\u002Fuploaded' },\n  async ({ event, step }) => {\n    \u002F\u002F Step 1: Extract text (may take a while)\n    const text = await step.run('extract-text', async () => {\n      return await extractTextFromPDF(event.data.fileUrl);\n    });\n\n    \u002F\u002F Step 2: Chunk for embedding\n    const chunks = await step.run('chunk-text', async () => {\n      return chunkText(text, { maxTokens: 500 });\n    });\n\n    \u002F\u002F Step 3: Generate embeddings (API rate limited)\n    const embeddings = await step.run('generate-embeddings', async () => {\n      return await openai.embeddings.create({\n        model: 'text-embedding-3-small',\n        input: chunks,\n      });\n    });\n\n    \u002F\u002F Step 4: Store in vector DB\n    await step.run('store-vectors', async () => {\n      await vectorDb.upsert({\n        vectors: embeddings.data.map((e, i) => ({\n          id: `${event.data.documentId}-${i}`,\n          values: e.embedding,\n          metadata: { chunk: chunks[i] },\n        })),\n      });\n    });\n\n    return { chunks: chunks.length, status: 'indexed' };\n  }\n);\n\n## Validation Checks\n\n### Inngest serve handler present\n\nSeverity: CRITICAL\n\nMessage: Inngest requires a serve handler to receive events\n\nFix action: Create app\u002Fapi\u002Finngest\u002Froute.ts with serve() export\n\n### Functions registered with serve\n\nSeverity: ERROR\n\nMessage: Ensure all Inngest functions are registered in the serve() call\n\nFix action: Add function to the functions array in serve()\n\n### Step.run has descriptive name\n\nSeverity: WARNING\n\nMessage: Step names should be kebab-case and descriptive\n\nFix action: Use descriptive step names like 'fetch-user' or 'send-email'\n\n### waitForEvent has timeout\n\nSeverity: ERROR\n\nMessage: waitForEvent should have a timeout to prevent infinite waits\n\nFix action: Add timeout option: { timeout: '24h' }\n\n### Function has concurrency limit\n\nSeverity: WARNING\n\nMessage: Consider adding concurrency limits to protect downstream services\n\nFix action: Add concurrency: { limit: 10 } to function config\n\n### Event types defined\n\nSeverity: WARNING\n\nMessage: Inngest client should define event schemas for type safety\n\nFix action: Add schemas: new EventSchemas().fromRecord\u003CEvents>()\n\n### Function has unique ID\n\nSeverity: CRITICAL\n\nMessage: Every Inngest function must have a unique ID\n\nFix action: Add id: 'my-function-name' to function config\n\n### Sleep uses duration string\n\nSeverity: WARNING\n\nMessage: step.sleep should use duration strings like '1h' or '30m', not milliseconds\n\nFix action: Use duration string: step.sleep('wait', '1h')\n\n### Retry policy configured\n\nSeverity: WARNING\n\nMessage: Consider configuring retry policy for failure handling\n\nFix action: Add retries: 3 or retries: { attempts: 3, backoff: { ... } }\n\n### Idempotency key for payment functions\n\nSeverity: ERROR\n\nMessage: Payment-related functions should use idempotency keys\n\nFix action: Add idempotency: 'event.data.orderId' to function config\n\n## Collaboration\n\n### Delegation Triggers\n\n- redis|queue infrastructure|bullmq -> bullmq-specialist (Need Redis-based queue with existing infrastructure)\n- saga|compensation|rollback|long-running workflow -> temporal-craftsman (Need complex workflow orchestration with compensation)\n- event sourcing|event store|cqrs -> event-architect (Need event sourcing patterns)\n- vercel|deploy|production -> vercel-deployment (Need deployment configuration)\n- database|schema|data model -> supabase-backend (Need database for event data)\n- api|endpoint|route -> backend (Need API to trigger events)\n\n### Vercel Background Jobs\n\nSkills: inngest, nextjs-app-router, vercel-deployment\n\nWorkflow:\n\n```\n1. Define Inngest functions (inngest)\n2. Set up serve handler in Next.js (nextjs-app-router)\n3. Configure function timeouts (vercel-deployment)\n4. Deploy and test (vercel-deployment)\n```\n\n### AI Pipeline\n\nSkills: inngest, ai-agents-architect, supabase-backend\n\nWorkflow:\n\n```\n1. Design AI workflow steps (ai-agents-architect)\n2. Implement with Inngest durability (inngest)\n3. Store results in database (supabase-backend)\n4. Handle retries for API failures (inngest)\n```\n\n### Webhook Processing\n\nSkills: inngest, stripe-integration, backend\n\nWorkflow:\n\n```\n1. Receive webhook (backend)\n2. Send to Inngest with idempotency (inngest)\n3. Process payment logic (stripe-integration)\n4. Update application state (backend)\n```\n\n### Email Automation\n\nSkills: inngest, email-systems, supabase-backend\n\nWorkflow:\n\n```\n1. Trigger event from user action (inngest)\n2. Schedule drip emails with step.sleep (inngest)\n3. Send emails with retry (email-systems)\n4. Track email status (supabase-backend)\n```\n\n### Scheduled Tasks\n\nSkills: inngest, backend, analytics-architecture\n\nWorkflow:\n\n```\n1. Define cron triggers (inngest)\n2. Implement processing logic (backend)\n3. Aggregate and report data (analytics-architecture)\n4. Handle failures with alerting (inngest)\n```\n\n## Related Skills\n\nWorks well with: `nextjs-app-router`, `vercel-deployment`, `supabase-backend`, `email-systems`, `ai-agents-architect`, `stripe-integration`\n\n## When to Use\n- User mentions or implies: inngest\n- User mentions or implies: serverless background job\n- User mentions or implies: event-driven workflow\n- User mentions or implies: step function\n- User mentions or implies: durable execution\n- User mentions or implies: vercel background job\n- User mentions or implies: scheduled function\n- User mentions or implies: fan out\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.\n","","imported","https:\u002F\u002Fgithub.com\u002Fsickn33\u002Fantigravity-awesome-skills","user_system_seed","SkillOPIC",true,235,1113,"2026-05-16 13:23:47",{"id":8,"name":21,"slug":22,"icon":23,"description":24,"sort":25,"createdAt":26},"其他","other","mdi-page-next-outline","其他类型Skill",5,"2026-05-16 12:53:40",{"id":7,"name":28,"slug":29,"icon":30,"description":31,"moduleId":8,"sort":32,"skillCount":33,"createdAt":26},"职场发展","career","mdi-briefcase-outline","面试准备、简历优化、职业规划",4,575,[35],{"id":36,"skillId":4,"version":37,"fileName":38,"fileSize":39,"filePath":40,"fileHash":41,"manifest":42,"createdAt":19},"63fcbf84-2f03-414d-8daa-66356b07660e","1.0.0","inngest.zip",4415,"uploads\u002Fskills\u002Ffc5ed895-ffac-4027-94dc-d51bb27a78bb\u002Finngest.zip","d0a0f32ab26bb4f3f996f311251ed3805fc174c228f728c66605c997e4e9f714","[{\"path\":\"SKILL.md\",\"isDirectory\":false,\"size\":12417}]",{"code":44,"message":45,"data":46},200,"success",{"items":47,"stats":48,"page":51},[],{"averageRating":49,"totalRatings":49,"ratingCounts":50},0,[49,49,49,49,49],{"limit":52,"offset":49,"hasMore":53,"nextOffset":52,"ratedOnly":16},15,false]