Background jobs in Node.js usually mean BullMQ + Redis + worker processes + monitoring. Trigger.dev gives you durable, long-running jobs with retries, scheduling, and observability — all in TypeScript, deployed alongside your app.
What Trigger.dev Gives You for Free
- 50,000 runs/month on free tier
- Durable execution — jobs survive server restarts
- Automatic retries — with exponential backoff
- Scheduling — cron jobs and delayed execution
- Concurrency control — rate limiting and queues
- Real-time logs — observe every step in the dashboard
- Long-running tasks — up to 5 minutes on free, hours on paid
Quick Start
npx trigger.dev@latest init
Define a Task
// trigger/send-welcome-email.ts
import { task } from '@trigger.dev/sdk/v3';
import { Resend } from 'resend';
export const sendWelcomeEmail = task({
id: 'send-welcome-email',
retry: { maxAttempts: 3 },
run: async (payload: { userId: string; email: string }) => {
const user = await db.users.find(payload.userId);
const resend = new Resend(process.env.RESEND_KEY);
await resend.emails.send({
from: 'hello@myapp.com',
to: payload.email,
subject: `Welcome, ${user.name}!`,
html: renderWelcomeEmail(user)
});
return { sent: true };
}
});
Trigger From Your App
// In your Next.js API route or server action
import { sendWelcomeEmail } from '@/trigger/send-welcome-email';
export async function createUser(data: CreateUserInput) {
const user = await db.users.create({ data });
// Trigger background job — returns immediately
await sendWelcomeEmail.trigger({ userId: user.id, email: user.email });
return user; // User gets instant response
}
Multi-Step Tasks
import { task, wait } from '@trigger.dev/sdk/v3';
export const onboardUser = task({
id: 'onboard-user',
run: async (payload: { userId: string }) => {
// Step 1: Send welcome email
await sendWelcomeEmail.triggerAndWait({ userId: payload.userId, email: '...' });
// Step 2: Wait 1 day
await wait.for({ days: 1 });
// Step 3: Send tips email
await sendTipsEmail.trigger({ userId: payload.userId });
// Step 4: Wait 3 days
await wait.for({ days: 3 });
// Step 5: Check engagement
const user = await db.users.find(payload.userId);
if (!user.hasLoggedIn) {
await sendReEngagementEmail.trigger({ userId: payload.userId });
}
}
});
Scheduled Tasks (Cron)
import { schedules } from '@trigger.dev/sdk/v3';
export const dailyReport = schedules.task({
id: 'daily-report',
cron: '0 9 * * *', // Every day at 9 AM
run: async () => {
const stats = await generateDailyStats();
await sendReportEmail(stats);
}
});
Concurrency Control
export const processImage = task({
id: 'process-image',
queue: { concurrencyLimit: 5 }, // Max 5 parallel runs
retry: { maxAttempts: 3 },
run: async (payload: { imageUrl: string }) => {
const result = await sharp(payload.imageUrl)
.resize(800, 600)
.toBuffer();
await storage.upload(result);
}
});
Trigger.dev vs BullMQ vs Inngest
| Feature | Trigger.dev | BullMQ | Inngest |
|---|---|---|---|
| Infrastructure | Managed | Self-hosted Redis | Managed |
| Language | TypeScript | Node.js | TypeScript |
| Durable execution | Yes | No | Yes |
| Dashboard | Built-in | Bull Board | Built-in |
| Free tier | 50K runs/mo | Self-hosted | 25K events |
| Long-running | Yes (hours) | Yes | 60s limit (free) |
| Setup time | 5 minutes | 30+ minutes | 10 minutes |
The Verdict
Trigger.dev makes background jobs in TypeScript as easy as writing a function. Durable execution, automatic retries, scheduling, and real-time observability — no Redis, no worker management, just code.
Need help building production web scrapers or data pipelines? I build custom solutions. Reach out: spinov001@gmail.com
Check out my awesome-web-scraping collection — 400+ tools for extracting web data.
Top comments (0)