General
Background Tasks

Inngest

Integrate Inngest with your application for event-driven background workflows.

Inngest is a developer-first platform for building reliable background jobs, scheduled functions, and event-driven workflows. It provides a simple API for creating durable functions that automatically retry on failure.

Setup

Visit Inngest and create a free account. Create a new app and note down your signing key.

Add your Inngest credentials to your environment variables:

.env
INNGEST_EVENT_KEY=your_event_key_here
INNGEST_SIGNING_KEY=your_signing_key_here

For production, make sure to add these environment variables to your deployment platform.

Install dependencies

Install the Inngest SDK:

Terminal
npm install inngest

Configure Inngest

Create an Inngest client:

lib/inngest.ts
import { Inngest } from 'inngest';

import { env } from '@/lib/env';

export const inngest = new Inngest({
  id: 'your-app-id',
  eventKey: env.INNGEST_EVENT_KEY
});

Create your first function

Create functions in a lib/inngest/functions directory:

lib/inngest/functions/process-user-data.ts
import { inngest } from '@/lib/inngest';

export const processUserData = inngest.createFunction(
  { id: 'process-user-data' },
  { event: 'user/data.process' },
  async ({ event, step }) => {
    const { userId, operation } = event.data;

    await step.run('process-data', async () => {
      console.log('Processing user data', { userId, operation });

      switch (operation) {
        case 'export':
          // Export user data
          await new Promise((resolve) => setTimeout(resolve, 2000));
          return { success: true, result: 'Data exported to CSV' };

        case 'analyze':
          // Analyze user data
          await new Promise((resolve) => setTimeout(resolve, 5000));
          return {
            success: true,
            result: { totalActions: 156, avgSessionTime: '4m 32s' }
          };

        case 'cleanup':
          // Cleanup user data
          await new Promise((resolve) => setTimeout(resolve, 3000));
          return { success: true, result: 'Removed 23 obsolete records' };

        default:
          throw new Error(`Unknown operation: ${operation}`);
      }
    });
  }
);

Create a scheduled function:

lib/inngest/functions/daily-cleanup.ts
import { inngest } from '@/lib/inngest';

export const dailyCleanup = inngest.createFunction(
  { id: 'daily-cleanup' },
  { cron: '0 2 * * *' }, // Daily at 2 AM
  async ({ step }) => {
    await step.run('cleanup-logs', async () => {
      console.log('Cleaning up old logs');
      await new Promise((resolve) => setTimeout(resolve, 5000));
      return { logsCleaned: true };
    });

    await step.run('cleanup-temp-files', async () => {
      console.log('Cleaning up temporary files');
      await new Promise((resolve) => setTimeout(resolve, 3000));
      return { tempFilesCleaned: true };
    });

    await step.run('generate-reports', async () => {
      console.log('Generating daily reports');
      await new Promise((resolve) => setTimeout(resolve, 8000));
      return { reportsGenerated: true };
    });
  }
);

Register functions

Create an API route to serve your Inngest functions:

app/api/inngest/route.ts
import { serve } from 'inngest/next';

import { inngest } from '@/lib/inngest';
import { dailyCleanup } from '@/lib/inngest/functions/daily-cleanup';
import { processUserData } from '@/lib/inngest/functions/process-user-data';

export const { GET, POST, PUT } = serve({
  client: inngest,
  functions: [processUserData, dailyCleanup]
});

Triggering functions

From an API route

app/api/tasks/process-user-data/route.ts
import { NextRequest, NextResponse } from 'next/server';
import * as z from 'zod';

import { getSession } from '@/lib/auth/server';
import { inngest } from '@/lib/inngest';

const processUserDataSchema = z.object({
  userId: z.string(),
  operation: z.enum(['export', 'analyze', 'cleanup'])
});

export async function POST(request: NextRequest) {
  const session = await getSession();

  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const body = await request.json();
  const { userId, operation } = processUserDataSchema.parse(body);

  await inngest.send({
    name: 'user/data.process',
    data: { userId, operation }
  });

  return NextResponse.json({
    success: true,
    message: 'Background task started successfully'
  });
}

From a server action

app/actions/user-actions.ts
'use server';

import { getSession } from '@/lib/auth/server';
import { inngest } from '@/lib/inngest';

export async function processUserData(
  userId: string,
  operation: 'export' | 'analyze' | 'cleanup'
) {
  const session = await getSession();

  if (!session) {
    throw new Error('Unauthorized');
  }

  try {
    await inngest.send({
      name: 'user/data.process',
      data: { userId, operation }
    });

    return { success: true };
  } catch (error) {
    console.error('Failed to trigger background task:', error);
    throw new Error('Failed to start background task');
  }
}

Monitoring and debugging

Visit the Inngest Dashboard to monitor your functions:

  • View function execution logs and performance metrics
  • Track success and failure rates
  • Monitor function duration and step execution
  • Replay failed functions
  • Set up alerts for function failures

Best practices

Use step functions for reliability

Break your function into steps using step.run() to make it more reliable and debuggable:

await step.run('step-name', async () => {
  // This step will be retried independently if it fails
  return await processData();
});

Use descriptive function IDs

// ✅ Good
{
  id: 'user-data-export-csv';
}

// ❌ Not so good
{
  id: 'task1';
}

Handle errors gracefully

await step.run('process', async () => {
  try {
    return await processData();
  } catch (error) {
    console.error('Processing failed:', error);
    throw error; // Re-throw to trigger retry
  }
});

Next steps

With Inngest integrated into your application, you can now:

  • Build reliable background jobs with automatic retries
  • Schedule recurring tasks with cron expressions
  • Create event-driven workflows that respond to events
  • Compose complex workflows using step functions

Ready to explore more? Check out the official documentation for advanced features like function composition, event filtering, and more.