Claude Skills Guide

Claude Code Inngest Durable Workflow for Long Running Tasks

Building applications that handle long-running tasks is a common challenge in modern software development. Whether you’re processing large datasets, running AI model inference, or coordinating complex multi-step workflows, you need a reliable way to manage state and ensure tasks complete successfully even when interruptions occur. This guide explores how to combine Claude Code with Inngest to create robust, durable workflows that handle long-running tasks with ease.

Understanding Durable Workflows

Traditional request-response HTTP patterns work well for quick operations, but many real-world tasks take minutes, hours, or even days to complete. Durable workflows solve this problem by providing a framework for:

Inngest is a developer-friendly durable workflow platform that integrates smoothly with your existing codebase. It handles the complexity of state management, allowing you to write business logic without worrying about infrastructure.

Why Combine Claude Code with Inngest?

Claude Code brings powerful AI-assisted development capabilities to your workflow. When you combine Claude Code with Inngest, you get:

  1. AI-powered workflow design: Claude Code can help design and implement complex workflow logic
  2. Natural language debugging: Describe issues in plain English and get actionable guidance
  3. Code generation: Quickly scaffold Inngest workflow definitions and step functions
  4. Documentation assistance: Generate documentation for your workflow states and events

Getting Started with Inngest

First, install the Inngest SDK and set up your project:

npm install inngest

Create an Inngest client configuration:

import { Inngest } from 'inngest';
import { serve } from 'inngest/next';

const inngest = new Inngest({
  name: 'MyApp',
  eventKey: process.env.INNGEST_EVENT_KEY,
});

Building Your First Durable Workflow

Let’s create a workflow that processes a batch of items with multiple steps. This demonstrates key patterns for long-running tasks:

// Define the workflow steps
const processItemStep = inngest.step({
  id: 'process-item',
  name: 'Process individual item',
}, async ({ item }, { stepRunId }) => {
  // Your processing logic here
  const result = await processItem(item);
  return { success: true, result };
});

const notifyCompletionStep = inngest.step({
  id: 'notify-completion',
  name: 'Send completion notification',
}, async ({ userId, totalProcessed }, { stepRunId }) => {
  await sendNotification(userId, `Processed ${totalProcessed} items`);
  return { notified: true };
});

// Define the workflow
const batchProcessWorkflow = inngest.createWorkflow(
  'batch-process',
  {
    id: 'batch-process',
    on: 'batch/process.started',
  },
  async ({ event }) => {
    const items = event.data.items;
    const results = [];

    // Process each item, handling failures gracefully
    for (const item of items) {
      const result = await processItemStep.run({ item });
      results.push(result);
    }

    // Notify completion
    await notifyCompletionStep.run({
      userId: event.data.userId,
      totalProcessed: results.length,
    });

    return { completed: true, total: results.length };
  }
);

Handling Long-Running Operations

For operations that take significant time, Inngest provides the sleep and waitForEvent primitives. Here’s how to use them with Claude Code assistance:

const aiAnalysisWorkflow = inngest.createWorkflow(
  'ai-analysis',
  {
    id: 'ai-analysis',
    on: 'analysis/request',
  },
  async ({ event }) => {
    // Step 1: Queue the analysis job
    const job = await inngest.step.run('queue-analysis', async () => {
      return await queueAnalysisJob(event.data);
    });

    // Step 2: Wait for completion (could be minutes to hours)
    const result = await inngest.step.waitForEvent('wait-for-result', {
      event: 'analysis/completed',
      timeout: '24h', // Long timeout for async processing
      match: `data.jobId == "${job.id}"`,
    });

    if (!result) {
      // Handle timeout - could retry or notify
      return { status: 'timeout', jobId: job.id };
    }

    return { status: 'completed', result: result.data };
  }
);

Error Handling and Retries

Claude Code can help you design robust error handling strategies. Here’s a pattern for implementing retries with backoff:

const robustProcessingStep = inngest.step({
  id: 'robust-process',
  name: 'Process with retry',
  retry: {
    attempts: 3,
    delay: '2s',
    backoff: 'exponential',
  },
}, async ({ data }, { stepRunId, context }) => {
  const attempt = context?.attempt || 1;
  
  try {
    return await processWithPotentialFailure(data);
  } catch (error) {
    // Claude Code can help analyze error patterns
    if (error.code === 'RATE_LIMIT') {
      // Handle rate limiting specifically
      throw new RetryError('Rate limited, will retry');
    }
    throw error;
  }
});

Best Practices for Claude Code + Inngest Integration

1. Use Descriptive Step IDs

Claude Code works best when your code is well-structured. Use clear, descriptive IDs for steps:

// Good: Descriptive IDs help Claude understand the flow
inngest.step({ id: 'validate-user-input', name: 'Validate user input' })

// Avoid: Generic IDs make debugging harder
inngest.step({ id: 'step1', name: 'Step 1' })

2. Document State Transitions

Add comments that help Claude Code understand your workflow logic:

// WORKFLOW: Order Processing
// States: pending -> payment_confirmed -> processing -> shipped -> delivered
// Events: order.placed, payment.confirmed, item.shipped, delivery.completed
// Retries: payment confirmation has 3 retries with 5s delay

3. Implement Checkpointing for Very Long Tasks

For tasks running over days or weeks, implement explicit checkpoints:

const longRunningWorkflow = inngest.createWorkflow(
  'data-migration',
  { id: 'data-migration', on: 'migration/start' },
  async ({ event }) => {
    const { totalRecords } = event.data;
    const batchSize = 1000;
    let processed = 0;

    while (processed < totalRecords) {
      // Process batch
      const batchResult = await processBatch.run({
        offset: processed,
        limit: batchSize,
      });

      // Create checkpoint - allows resume if interrupted
      await inngest.step.waitForEvent('checkpoint', {
        event: 'migration.checkpoint',
        timeout: '1h',
        match: `data.offset == ${processed + batchSize}`,
      });

      processed += batchResult.processed;
    }

    return { migrated: processed };
  }
);

Monitoring and Debugging

Inngest provides a dashboard to monitor workflow execution. Combine this with Claude Code’s debugging capabilities:

  1. Check workflow status: Describe the error to Claude Code and get potential causes
  2. Analyze logs: Use Claude Code to interpret Inngest execution logs
  3. Plan recovery: Ask Claude Code for strategies to recover from specific failure states

Conclusion

Combining Claude Code with Inngest gives you a powerful toolkit for building reliable long-running task systems. Inngest handles the complexity of durable execution, while Claude Code assists with design, implementation, and debugging. Start with simple workflows and progressively adopt more advanced patterns as your needs grow.

The key is to use Claude Code’s strengths—understanding intent, generating code, and explaining patterns—while letting Inngest handle the heavy lifting of workflow state management. Together, they make building robust, long-running task systems accessible to developers at any level.

Built by theluckystrike — More at zovo.one