Inngest lets you build reliable event-driven functions with retries, scheduling, and step functions — all from your existing codebase. No queues, no workers, no infrastructure.
Why Inngest?
- Zero infrastructure — no Redis, no queues, no workers to manage
- Step functions — each step is individually retryable
- Event-driven — trigger functions from events, webhooks, schedules
- Works everywhere — Next.js, Express, Remix, Deno, Bun
Quick Start
npm install inngest
npx inngest-cli@latest dev # Local dev server
import { Inngest } from 'inngest';
const inngest = new Inngest({ id: 'my-app' });
// Define a function
export const sendWelcomeEmail = inngest.createFunction(
{ id: 'send-welcome-email' },
{ event: 'user/created' },
async ({ event, step }) => {
// Step 1: Get user details
const user = await step.run('get-user', async () => {
return await db.getUser(event.data.userId);
});
// Step 2: Send email
await step.run('send-email', async () => {
await email.send({
to: user.email,
subject: 'Welcome!',
body: `Hello ${user.name}!`,
});
});
// Step 3: Update CRM
await step.run('update-crm', async () => {
await crm.addContact(user);
});
}
);
Trigger Events
// From your API route
await inngest.send({
name: 'user/created',
data: { userId: '123', plan: 'pro' },
});
// Batch events
await inngest.send([
{ name: 'order/placed', data: { orderId: '1' } },
{ name: 'order/placed', data: { orderId: '2' } },
]);
Scheduled Functions (Cron)
export const dailyCleanup = inngest.createFunction(
{ id: 'daily-cleanup' },
{ cron: '0 2 * * *' }, // Every day at 2 AM
async ({ step }) => {
await step.run('clean-expired', async () => {
return await db.deleteExpiredSessions();
});
await step.run('send-report', async () => {
await slack.send('#ops', 'Cleanup complete');
});
}
);
Wait for Events
export const onboardingFlow = inngest.createFunction(
{ id: 'onboarding' },
{ event: 'user/created' },
async ({ event, step }) => {
// Send welcome email immediately
await step.run('welcome-email', () => sendWelcome(event.data.userId));
// Wait up to 24h for profile completion
const profileEvent = await step.waitForEvent('wait-for-profile', {
event: 'user/profile-completed',
match: 'data.userId',
timeout: '24h',
});
if (!profileEvent) {
// Profile not completed — send reminder
await step.run('send-reminder', () => sendReminder(event.data.userId));
}
}
);
Delayed Steps
export const trialReminder = inngest.createFunction(
{ id: 'trial-reminder' },
{ event: 'trial/started' },
async ({ event, step }) => {
// Wait 7 days
await step.sleep('wait-7-days', '7d');
// Check if converted
const user = await step.run('check-user', () => getUser(event.data.userId));
if (!user.isPaid) {
await step.run('send-reminder', () => sendTrialReminder(user));
}
}
);
Need event-driven data pipelines? Check out my Apify actors for web scraping that triggers on events, or email spinov001@gmail.com for custom workflows.
Inngest, Trigger.dev, or Temporal — how do you handle workflows? Share below!
Top comments (0)