DEV Community

Alex Spinov
Alex Spinov

Posted on

Inngest Has a Free API — Background Jobs and Workflows Without Infrastructure

TL;DR

Inngest is a developer platform for background jobs, scheduled functions, and durable workflows. Write functions in your existing codebase — Inngest handles queuing, retries, concurrency, and scheduling. Free tier: 50K events/month.

What Is Inngest?

Inngest makes background work simple:

  • Event-driven — trigger functions from events
  • Durable execution — survives crashes and restarts
  • Step functions — multi-step workflows with automatic retries
  • Scheduling — cron jobs and delayed execution
  • Concurrency control — rate limiting and throttling
  • Zero infrastructure — no queues, no workers to manage
  • Free tier — 50K events/month

Quick Start

npm install inngest
Enter fullscreen mode Exit fullscreen mode

Define Functions

import { Inngest } from "inngest";

const inngest = new Inngest({ id: "my-app" });

// Simple background function
export const sendWelcomeEmail = inngest.createFunction(
  { id: "send-welcome-email" },
  { event: "user/created" },
  async ({ event, step }) => {
    // Step 1: Send welcome email
    await step.run("send-email", async () => {
      await emailService.send({
        to: event.data.email,
        template: "welcome",
        name: event.data.name,
      });
    });

    // Step 2: Wait 3 days
    await step.sleep("wait-3-days", "3 days");

    // Step 3: Send follow-up
    await step.run("send-followup", async () => {
      await emailService.send({
        to: event.data.email,
        template: "getting-started",
      });
    });

    // Step 4: Wait 7 more days, then check engagement
    await step.sleep("wait-7-days", "7 days");

    const isActive = await step.run("check-engagement", async () => {
      return await analytics.isUserActive(event.data.userId);
    });

    if (!isActive) {
      await step.run("send-reengagement", async () => {
        await emailService.send({
          to: event.data.email,
          template: "we-miss-you",
        });
      });
    }
  }
);
Enter fullscreen mode Exit fullscreen mode

Trigger Events

// From your API route
await inngest.send({
  name: "user/created",
  data: {
    userId: user.id,
    email: user.email,
    name: user.name,
  },
});
Enter fullscreen mode Exit fullscreen mode

Serve with Next.js

// app/api/inngest/route.ts
import { serve } from "inngest/next";
import { inngest } from "@/lib/inngest";
import { sendWelcomeEmail } from "@/inngest/functions";

export const { GET, POST, PUT } = serve({
  client: inngest,
  functions: [sendWelcomeEmail],
});
Enter fullscreen mode Exit fullscreen mode

Cron Jobs

export const dailyReport = inngest.createFunction(
  { id: "daily-report" },
  { cron: "0 9 * * *" }, // Every day at 9 AM
  async ({ step }) => {
    const metrics = await step.run("fetch-metrics", async () => {
      return await analytics.getDailyMetrics();
    });

    await step.run("send-report", async () => {
      await slack.postMessage({
        channel: "#metrics",
        text: formatReport(metrics),
      });
    });
  }
);
Enter fullscreen mode Exit fullscreen mode

Concurrency and Throttling

export const processOrder = inngest.createFunction(
  {
    id: "process-order",
    concurrency: { limit: 10 }, // Max 10 concurrent
    throttle: { limit: 100, period: "1m" }, // Max 100/minute
    retries: 3,
  },
  { event: "order/placed" },
  async ({ event, step }) => {
    // Process with automatic retry on failure
    await step.run("charge", () => payment.charge(event.data));
    await step.run("fulfill", () => warehouse.ship(event.data));
    await step.run("notify", () => email.sendConfirmation(event.data));
  }
);
Enter fullscreen mode Exit fullscreen mode

Fan-out Pattern

export const processCSV = inngest.createFunction(
  { id: "process-csv" },
  { event: "csv/uploaded" },
  async ({ event, step }) => {
    const rows = await step.run("parse", async () => {
      return await parseCSV(event.data.fileUrl);
    });

    // Fan out: send an event for each row
    await step.sendEvent(
      "fan-out",
      rows.map((row) => ({
        name: "csv/row-process",
        data: row,
      }))
    );
  }
);
Enter fullscreen mode Exit fullscreen mode

Inngest vs Alternatives

Feature Inngest BullMQ Temporal AWS Step Functions
Free tier 50K events Self-host Self-host 4K transitions
Infrastructure None Redis Temporal Server AWS
Language TS/JS/Python/Go Node.js Multi JSON (ASL)
Step functions Yes Manual Yes Yes
Concurrency control Built-in Built-in Manual Manual
Local dev npx inngest-cli dev Redis needed Full server SAM

Resources


Building automated data pipelines? My Apify scraping tools extract web data on demand — orchestrate them with Inngest for event-driven data workflows. Questions? Email spinov001@gmail.com

Top comments (0)