trigger-jobs
npx skills add https://github.com/retrip-ai/agent-skills --skill trigger-jobs
Agent 安装分布
Skill 文档
Trigger.dev Background Jobs
Complete guide to background jobs, queues, and rate limiting using Trigger.dev.
Overview
This skill covers:
- Task Creation – Defining background jobs with Trigger.dev SDK
- Queue Management – Concurrency limits and rate limiting
- Job Triggering – From tRPC, Better Auth, or anywhere in code
- Email Tasks – Using JSX for React Email templates
- Advanced Patterns – Delays, subtasks, batch processing
When to Apply
Reference these guidelines when:
- Creating background jobs or async tasks
- Sending emails asynchronously
- Implementing rate-limited operations
- Building scheduled or delayed tasks
- Processing batch operations
- Integrating jobs with tRPC or Better Auth
Quick Reference
Basic Task
import { task } from '@trigger.dev/sdk/v3';
export const myTask = task({
id: 'my-task',
queue: {
name: 'default',
concurrencyLimit: 5,
},
run: async (payload: { data: string }, { ctx }) => {
console.log('Executing:', payload.data);
// Your logic here
return { success: true };
},
});
Email Task (with JSX)
// Use .tsx extension
import { emailClient } from '@retrip-ai/email';
import { MyEmail } from '@retrip-ai/email/emails/my-email';
import { task } from '@trigger.dev/sdk/v3';
export const sendEmailTask = task({
id: 'send-email',
queue: {
name: 'emails',
concurrencyLimit: 2, // Respect Resend rate limit
},
run: async (payload: { to: string; subject: string }) => {
await emailClient.emails.send({
from: 'Retrip <platform@no-reply.retrip.io>',
to: payload.to,
subject: payload.subject,
react: <MyEmail />,
});
return { success: true };
},
});
Triggering Tasks
import { myTask } from '@retrip-ai/jobs';
// Trigger and don't wait
await myTask.trigger({ data: 'value' });
// Trigger and wait for result
const handle = await myTask.trigger({ data: 'value' });
const result = await handle.result();
Project Structure
packages/jobs/
âââ .env # Environment variables
âââ trigger.config.ts # Trigger.dev configuration
âââ src/
âââ index.ts # Package exports
âââ tasks/
âââ index.ts
âââ my-task.tsx
References
Complete documentation with examples:
references/jobs.md– Task creation, triggers, queues, rate limiting, advanced patterns
To find specific patterns:
grep -l "concurrencyLimit" references/*.md
grep -l "subtasks" references/*.md
grep -l "email" references/*.md
Core Patterns
1. Basic Task Creation
File: packages/jobs/src/tasks/my-task.ts
import { task } from '@trigger.dev/sdk/v3';
import { z } from 'zod';
const payloadSchema = z.object({
userId: z.string(),
message: z.string(),
});
export const myTask = task({
id: 'my-task',
queue: {
name: 'default',
concurrencyLimit: 5, // Max 5 simultaneous executions
},
retry: {
maxAttempts: 3,
minTimeoutInMs: 1000,
maxTimeoutInMs: 10000,
factor: 2,
},
run: async (payload, { ctx }) => {
const validated = payloadSchema.parse(payload);
console.log('Executing task:', validated);
console.log('Run ID:', ctx.run.id);
// Your business logic here
return { success: true, userId: validated.userId };
},
});
Export the task:
// packages/jobs/src/tasks/index.ts
export { myTask } from './my-task';
// packages/jobs/src/index.ts
export * from './tasks';
2. Email Task (with React Email)
IMPORTANT: Use .tsx extension for JSX support.
File: packages/jobs/src/tasks/send-invitation-email.tsx
import { emailClient } from '@retrip-ai/email';
import { InvitationEmail } from '@retrip-ai/email/emails/invitation';
import { task } from '@trigger.dev/sdk/v3';
import { z } from 'zod';
const payloadSchema = z.object({
to: z.string().email(),
invitedByName: z.string(),
teamName: z.string(),
invitationLink: z.string().url(),
});
export const sendInvitationEmailTask = task({
id: 'send-invitation-email',
queue: {
name: 'emails',
concurrencyLimit: 2, // Respect Resend rate limit (2/sec)
},
retry: {
maxAttempts: 3,
},
run: async (payload, { ctx }) => {
const validated = payloadSchema.parse(payload);
console.log(`Sending invitation to ${validated.to}`);
await emailClient.emails.send({
from: 'Retrip <platform@no-reply.retrip.io>',
to: validated.to,
subject: `${validated.invitedByName} invited you to ${validated.teamName}`,
react: (
<InvitationEmail
invitedByName={validated.invitedByName}
teamName={validated.teamName}
invitationLink={validated.invitationLink}
/>
),
});
console.log(`Invitation sent (Run ID: ${ctx.run.id})`);
return { success: true };
},
});
3. Triggering from Better Auth
// packages/auth-db/src/auth.ts
import { sendInvitationEmailTask } from '@retrip-ai/jobs';
organizationPlugin({
sendInvitationEmail: async (data) => {
await sendInvitationEmailTask.trigger({
to: data.email,
invitedByName: 'John Doe',
teamName: 'My Team',
invitationLink: data.link,
});
},
});
4. Triggering from tRPC
// apps/api-trpc/src/routers/notifications.ts
import { sendNotificationTask } from '@retrip-ai/jobs';
export const notificationsRouter = createTRPCRouter({
send: protectedProcedure
.input(z.object({ message: z.string() }))
.mutation(async ({ input }) => {
// Trigger and don't wait
await sendNotificationTask.trigger({
message: input.message,
});
return { queued: true };
}),
});
Queue Management and Rate Limiting
Concurrency Limits
Control how many instances of a task can run simultaneously:
export const myTask = task({
id: 'my-task',
queue: {
name: 'my-queue',
concurrencyLimit: 3, // Max 3 simultaneous executions
},
run: async (payload) => {
// When 3 tasks are running, the 4th waits in queue
},
});
Use cases:
- Rate limiting external APIs (e.g., Resend: 2/sec)
- Preventing database overload
- Respecting third-party service limits
Queue Behavior
All tasks with the same queue name share the same queue:
// Both tasks use the 'emails' queue
export const sendInvitationTask = task({
id: 'send-invitation',
queue: { name: 'emails', concurrencyLimit: 2 },
// ...
});
export const sendWelcomeTask = task({
id: 'send-welcome',
queue: { name: 'emails', concurrencyLimit: 2 },
// ...
});
When both tasks are triggered:
- Maximum 2 emails send at a time (shared limit)
- FIFO queue for waiting tasks
Retry Configuration
export const myTask = task({
id: 'my-task',
retry: {
maxAttempts: 3, // Total attempts (1 original + 2 retries)
minTimeoutInMs: 1000, // Start with 1s delay
maxTimeoutInMs: 10000, // Max 10s delay
factor: 2, // Exponential backoff (1s, 2s, 4s...)
randomize: true, // Add jitter
},
run: async (payload) => {
// If this throws, task will retry automatically
},
});
Advanced Patterns
Task with Delays
export const scheduledTask = task({
id: 'scheduled-task',
run: async (payload: { message: string }, { ctx }) => {
console.log('Starting task...');
// Wait 5 seconds
await ctx.wait.for({ seconds: 5 });
console.log('Continuing after delay...');
// Wait until specific time
await ctx.wait.until({ date: new Date('2025-01-15') });
return { success: true };
},
});
Batch Processing with Subtasks
export const batchEmailTask = task({
id: 'batch-email',
run: async (payload: { emails: string[] }) => {
console.log(`Sending to ${payload.emails.length} recipients`);
// Trigger multiple subtasks in parallel
const handles = await Promise.all(
payload.emails.map((email) =>
sendEmailTask.trigger({ to: email, subject: 'Hello' })
)
);
// Wait for all to complete
const results = await Promise.all(
handles.map((handle) => handle.result())
);
return { sent: results.length };
},
});
Conditional Execution
export const conditionalTask = task({
id: 'conditional-task',
run: async (payload: { shouldSendEmail: boolean }) => {
if (payload.shouldSendEmail) {
await sendEmailTask.trigger({ to: 'user@example.com', subject: 'Hello' });
}
return { executed: payload.shouldSendEmail };
},
});
Configuration
trigger.config.ts
import { defineConfig } from '@trigger.dev/sdk/v3';
export default defineConfig({
project: process.env.TRIGGER_PROJECT_ID || '',
dirs: ['./src/tasks'],
runtime: 'node',
logLevel: 'log',
maxDuration: 60, // Maximum execution time (seconds)
retries: {
enabledInDev: false,
default: {
maxAttempts: 3,
minTimeoutInMs: 1000,
maxTimeoutInMs: 10000,
factor: 2,
randomize: true,
},
},
});
Environment Variables
File: packages/jobs/.env (DO NOT commit)
TRIGGER_SECRET_KEY="tr_dev_xxx"
TRIGGER_PROJECT_ID="proj_xxx"
RESEND_API_KEY="re_xxx"
Get credentials from cloud.trigger.dev.
Local Development
Start Development Server
cd packages/jobs
bun run dev
This starts the local worker that executes tasks.
View Dashboard
Access cloud.trigger.dev to:
- View executed tasks
- Logs and errors
- Retry failed tasks
- Performance metrics
Deployment
cd packages/jobs
bun run deploy
Best Practices
â Do:
Task Structure:
- One file per task for clarity
- Use
.tsxextension for JSX (emails) - Validate payloads with Zod schemas
- Return meaningful structured data
Queues:
- Configure
concurrencyLimitto respect rate limits - Use descriptive queue names
- Group related tasks in same queue
Logging:
- Use
console.logwith useful context - Include Run ID in logs:
ctx.run.id - Log start and completion
Error Handling:
- Let errors throw for automatic retry
- Configure retry strategy appropriately
- Monitor dashboard for failures
Configuration:
- Don’t commit
.envfiles - Use environment variables for secrets
- Configure
maxDurationappropriately
â Don’t:
Task Structure:
- â Create tasks without validation
- â Use
.tsfor files with JSX - â Return unstructured data
- â Mix multiple responsibilities in one task
Queues:
- â Skip
concurrencyLimitfor rate-limited APIs - â Use same queue name for unrelated tasks
- â Set limits too high (respect API limits)
Error Handling:
- â Catch and ignore errors (prevents retry)
- â Skip error monitoring
- â Set
maxAttemptstoo high
Dependencies:
- â Forget to add native packages to
build.external - â Use packages not compatible with Node.js runtime
Common Patterns
Email with Validation
const payloadSchema = z.object({
to: z.string().email(),
subject: z.string().min(1).max(100),
});
export const sendEmailTask = task({
id: 'send-email',
queue: { name: 'emails', concurrencyLimit: 2 },
run: async (payload) => {
const validated = payloadSchema.parse(payload);
await emailClient.emails.send({
from: 'Retrip <platform@no-reply.retrip.io>',
to: validated.to,
subject: validated.subject,
react: <EmailTemplate />,
});
},
});
Trigger and Wait
const handle = await myTask.trigger({ data: 'value' });
const result = await handle.result(); // Blocks until complete
console.log(result); // { success: true }
Fire and Forget
await myTask.trigger({ data: 'value' });
// Continue without waiting
Troubleshooting
Error: Missing API key
Ensure .env has all required variables and the dev script loads them:
"dev": "export $(cat .env | xargs) && npx trigger.dev@latest dev"
Error: No trigger files found
Verify dirs in trigger.config.ts points to correct directory.
Error: JSX syntax error
Rename file from .ts to .tsx.
Rate limit exceeded
Reduce concurrencyLimit or add delays between executions.
Native Package Issues
Add to build.external in trigger.config.ts:
build: {
external: ['sharp', 'canvas', 'pino', 'my-native-package'],
},
Related Skills
tanstack-comprehensive– Triggering jobs from tRPC procedures
Version: 1.0.0 Last updated: 2026-01-14