> trigger-dev
You are an expert in Trigger.dev, the open-source background jobs platform for TypeScript. You help developers build reliable long-running tasks, scheduled jobs, webhook handlers, and event-driven workflows with automatic retries, concurrency control, real-time logs, and deployment to serverless infrastructure — replacing BullMQ/Redis setups with a fully managed or self-hosted solution purpose-built for modern TypeScript apps.
curl "https://skillshub.wtf/TerminalSkills/skills/trigger-dev?format=md"Trigger.dev — Background Jobs for TypeScript
You are an expert in Trigger.dev, the open-source background jobs platform for TypeScript. You help developers build reliable long-running tasks, scheduled jobs, webhook handlers, and event-driven workflows with automatic retries, concurrency control, real-time logs, and deployment to serverless infrastructure — replacing BullMQ/Redis setups with a fully managed or self-hosted solution purpose-built for modern TypeScript apps.
Core Capabilities
Task Definition
// trigger/tasks/process-order.ts
import { task, wait, logger, retry } from "@trigger.dev/sdk/v3";
export const processOrder = task({
id: "process-order",
retry: {
maxAttempts: 3,
factor: 2,
minTimeoutInMs: 1000,
maxTimeoutInMs: 30000,
},
run: async (payload: { orderId: string; userId: string }) => {
logger.info("Processing order", { orderId: payload.orderId });
// Step 1: Validate
const order = await db.orders.findById(payload.orderId);
if (!order) throw new Error(`Order ${payload.orderId} not found`);
// Step 2: Charge payment (auto-retries on failure)
const payment = await retry.onThrow(
async () => stripe.paymentIntents.create({
amount: order.total * 100,
currency: "usd",
customer: order.stripeCustomerId,
}),
{ maxAttempts: 3, randomize: true },
);
logger.info("Payment charged", { paymentId: payment.id });
// Step 3: Wait for webhook confirmation (durable wait)
const confirmation = await wait.for({ seconds: 300 });
// Step 4: Send notification
await sendEmail(order.userEmail, "order-confirmed", { orderId: order.id });
return { orderId: order.id, paymentId: payment.id, status: "completed" };
},
});
// Scheduled task (cron)
export const dailyReport = task({
id: "daily-report",
run: async () => {
const stats = await db.orders.aggregate({
today: { count: true, sum: "total" },
});
await sendSlackMessage("#reports", `Daily: ${stats.count} orders, $${stats.sum}`);
},
});
Triggering Tasks
// From your API route
import { tasks } from "@trigger.dev/sdk/v3";
// Trigger and forget
await tasks.trigger("process-order", {
orderId: "ord-123",
userId: "usr-456",
});
// Trigger and wait for result
const result = await tasks.triggerAndWait("process-order", {
orderId: "ord-123",
userId: "usr-456",
});
// Batch trigger
await tasks.batchTrigger("process-order", [
{ payload: { orderId: "ord-1", userId: "usr-1" } },
{ payload: { orderId: "ord-2", userId: "usr-2" } },
{ payload: { orderId: "ord-3", userId: "usr-3" } },
]);
// Schedule
await tasks.schedule("daily-report", {
cron: "0 9 * * *", // Daily at 9 AM
timezone: "America/New_York",
});
// Delayed
await tasks.trigger("send-reminder", {
userId: "usr-456",
}, {
delay: "24h", // Run in 24 hours
});
Webhook Handler
import { task } from "@trigger.dev/sdk/v3";
export const handleStripeWebhook = task({
id: "stripe-webhook",
run: async (payload: { type: string; data: any }) => {
switch (payload.type) {
case "payment_intent.succeeded":
await db.orders.update(payload.data.metadata.orderId, { status: "paid" });
await tasks.trigger("ship-order", { orderId: payload.data.metadata.orderId });
break;
case "customer.subscription.deleted":
await handleChurn(payload.data.customer);
break;
}
},
});
Installation
npx trigger.dev@latest init # Initialize in existing project
npx trigger.dev@latest dev # Local development
npx trigger.dev@latest deploy # Deploy to Trigger.dev cloud
Best Practices
- Idempotent tasks — Design tasks to be safely retried; use unique IDs for payment/email operations
- Structured logging — Use
logger.info/warn/errorwith objects; visible in Trigger.dev dashboard - Retry configuration — Set appropriate retry strategies per task; payment tasks need different retries than emails
- Concurrency limits — Set
concurrencyLimitto prevent overwhelming downstream services - Wait for events — Use
wait.forfor durable waits; task resumes even after server restarts - Batch for throughput — Use
batchTriggerfor bulk operations; processed in parallel with concurrency control - Environment separation — Use dev/staging/prod environments; preview deployments for testing
- Self-hosted option — Deploy Trigger.dev on your own infra with Docker; full control over data and execution
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> xero-accounting
Integrate with the Xero accounting API to sync invoices, expenses, bank transactions, and contacts — and generate financial reports like P&L and balance sheet. Use when: connecting apps to Xero, automating bookkeeping workflows, syncing accounting data, or pulling financial reports programmatically.
> windsurf-rules
Configure Windsurf AI coding assistant with .windsurfrules and workspace rules. Use when: customizing Windsurf for a project, setting AI coding standards, creating team-shared Windsurf configurations, or tuning Cascade AI behavior.