Jobs Package
Background job processing with Trigger.dev v3: scheduled tasks, retries, monitoring, and real-time execution tracking.
1. Get API Key
Section titled “1. Get API Key”- Create account at trigger.dev
- Create new project
- Copy API key from dashboard
2. Configure Environment
Section titled “2. Configure Environment”# Add to apps/api/.env.local
TRIGGER_SECRET_KEY=tr_dev_...
3. Start Development Server
Section titled “3. Start Development Server”# Run Trigger.dev CLI in watch mode
pnpm jobs
# Or with full filter
pnpm --filter @workspace/jobs jobs
This starts the Trigger.dev dev server which:
- ✅ Watches for job changes
- ✅ Auto-reloads on file save
- ✅ Connects to Trigger.dev dashboard
- ✅ Enables local job testing
Structure
Section titled “Structure”packages/jobs/
├── trigger/
│ ├── example.ts # Example job with wait
│ └── schedule.ts # Scheduled/cron jobs
├── trigger.config.ts # Trigger.dev configuration
└── package.json
Define Jobs
Section titled “Define Jobs”One-Time Tasks
Section titled “One-Time Tasks”Example: Send Email
// packages/jobs/trigger/send-email.ts
import { task, logger } from "@trigger.dev/sdk/v3";
export const sendEmail = task({
id: "send-email",
run: async (payload: { email: string; subject: string; body: string }) => {
logger.info("Sending email", { to: payload.email });
// Your email sending logic here
// e.g., using Resend, SendGrid, etc.
return {
success: true,
sentAt: new Date(),
};
},
});
With wait and retries:
import { task, wait } from "@trigger.dev/sdk/v3";
export const processOrder = task({
id: "process-order",
retry: {
maxAttempts: 3,
factor: 2,
},
run: async (payload: { orderId: string }) => {
// Step 1: Validate order
await validateOrder(payload.orderId);
// Wait 2 seconds before payment
await wait.for({ seconds: 2 });
// Step 2: Process payment
const payment = await processPayment(payload.orderId);
// Wait 5 seconds before fulfillment
await wait.for({ seconds: 5 });
// Step 3: Create fulfillment
await createFulfillment(payload.orderId);
return { status: "completed", orderId: payload.orderId };
},
});
Scheduled Tasks
Section titled “Scheduled Tasks”Example: Daily cleanup
// packages/jobs/trigger/cleanup.ts
import { schedules, logger } from "@trigger.dev/sdk/v3";
export const dailyCleanup = schedules.task({
id: "daily-cleanup",
cron: "0 2 * * *", // Every day at 2 AM UTC
run: async (payload) => {
logger.info("Running daily cleanup", {
scheduledFor: payload.timestamp,
timezone: payload.timezone,
});
// Cleanup old records
const deleted = await cleanupOldRecords();
return {
recordsDeleted: deleted,
completedAt: new Date(),
};
},
});
Common cron patterns:
0 * * * *
- Every hour at minute 00 0 * * *
- Every day at midnight0 9 * * 1-5
- Weekdays at 9 AM*/15 * * * *
- Every 15 minutes
Tool: crontab.guru - Visual cron editor
Trigger Jobs
Section titled “Trigger Jobs”From Your Code
Section titled “From Your Code”// In API routes, webhooks, or other backend code
import { sendEmail } from "@workspace/jobs/trigger/send-email";
export async function POST(request: Request) {
const { email, subject, body } = await request.json();
// Trigger job asynchronously
const handle = await sendEmail.trigger({
email,
subject,
body,
});
return Response.json({
message: "Email queued",
runId: handle.id,
});
}
Batch triggering:
import { sendEmail } from "@workspace/jobs/trigger/send-email";
// Trigger multiple jobs
const handles = await sendEmail.batchTrigger([
{ email: "user1@example.com", subject: "Welcome!" },
{ email: "user2@example.com", subject: "Welcome!" },
{ email: "user3@example.com", subject: "Welcome!" },
]);
From Trigger.dev Dashboard
Section titled “From Trigger.dev Dashboard”- Go to cloud.trigger.dev
- Select your project
- Navigate to “Tasks” tab
- Click on any task (e.g., “send-email”)
- Click “Test” button
- Enter payload JSON:
{ "email": "test@example.com", "subject": "Test Email", "body": "This is a test" }
- Click “Run test”
- Watch execution in real-time with logs
Monitor Runs
Section titled “Monitor Runs”View all job executions in the dashboard:
- ✅ Real-time logs - See
logger.info()
output live - ✅ Execution timeline - Visual breakdown of each step
- ✅ Retry history - See failed attempts and retries
- ✅ Performance metrics - Duration, memory usage
- ✅ Payload & output - Inspect input/output data
CLI Commands
Section titled “CLI Commands”Development
Section titled “Development”# Start dev server (watches for changes)
pnpm jobs
# Equivalent to:
npx trigger.dev@latest dev
Deployment
Section titled “Deployment”# Deploy to production
npx trigger.dev@latest deploy
# Deploy with environment
npx trigger.dev@latest deploy --env production
List Tasks
Section titled “List Tasks”# See all registered tasks
npx trigger.dev@latest list
Whoami
Section titled “Whoami”# Check current project and auth
npx trigger.dev@latest whoami
Configuration
Section titled “Configuration”trigger.config.ts
:
import type { TriggerConfig } from "@trigger.dev/sdk/v3";
export const config: TriggerConfig = {
project: "proj_xxxxx", // Your project ID
logLevel: "log",
maxDuration: 300, // Max 5 minutes per job
retries: {
enabledInDev: true,
default: {
maxAttempts: 3,
minTimeoutInMs: 1000,
maxTimeoutInMs: 10000,
factor: 2, // Exponential backoff
randomize: true,
},
},
};
Best Practices
Section titled “Best Practices”✅ Make Jobs Idempotent
Section titled “✅ Make Jobs Idempotent”Jobs should be safe to run multiple times:
export const createInvoice = task({
id: "create-invoice",
run: async (payload: { orderId: string }) => {
// Check if invoice already exists
const existing = await db.query.invoices.findFirst({
where: eq(invoices.orderId, payload.orderId),
});
if (existing) {
logger.info("Invoice already exists", { invoiceId: existing.id });
return existing;
}
// Create new invoice
const invoice = await db.insert(invoices).values({
orderId: payload.orderId,
// ... other fields
});
return invoice;
},
});
✅ Use Structured Logging
Section titled “✅ Use Structured Logging”// ❌ Bad - unstructured
logger.info(`Processing order ${orderId} for user ${userId}`);
// ✅ Good - structured
logger.info("Processing order", { orderId, userId, status: "started" });
✅ Handle Errors Gracefully
Section titled “✅ Handle Errors Gracefully”export const sendEmail = task({
id: "send-email",
retry: {
maxAttempts: 3,
},
run: async (payload) => {
try {
await emailProvider.send(payload);
return { success: true };
} catch (error) {
logger.error("Email send failed", { error, payload });
throw error; // Trigger will retry
}
},
});
✅ Keep Jobs Focused
Section titled “✅ Keep Jobs Focused”One job = one responsibility:
// ❌ Bad - does too much
export const onUserSignup = task({
run: async (payload) => {
await sendWelcomeEmail(payload.email);
await createStripeCustomer(payload.userId);
await sendSlackNotification(payload);
await updateAnalytics(payload);
},
});
// ✅ Good - separate jobs
export const sendWelcomeEmail = task({ ... });
export const createStripeCustomer = task({ ... });
export const sendSlackNotification = task({ ... });
// Chain them if needed
await sendWelcomeEmail.trigger({ email });
await createStripeCustomer.trigger({ userId });
Pricing
Section titled “Pricing”- Free tier: 100,000 runs/month
- Pro: $29/month for 1M runs
- Enterprise: Custom pricing
See trigger.dev/pricing for details.