trigger-jobs

📁 retrip-ai/agent-skills 📅 4 days ago
2
总安装量
2
周安装量
#63292
全站排名
安装命令
npx skills add https://github.com/retrip-ai/agent-skills --skill trigger-jobs

Agent 安装分布

trae 2
antigravity 2
claude-code 2
github-copilot 2
codex 2
kimi-cli 2

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 .tsx extension for JSX (emails)
  • Validate payloads with Zod schemas
  • Return meaningful structured data

Queues:

  • Configure concurrencyLimit to respect rate limits
  • Use descriptive queue names
  • Group related tasks in same queue

Logging:

  • Use console.log with 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 .env files
  • Use environment variables for secrets
  • Configure maxDuration appropriately

❌ Don’t:

Task Structure:

  • ❌ Create tasks without validation
  • ❌ Use .ts for files with JSX
  • ❌ Return unstructured data
  • ❌ Mix multiple responsibilities in one task

Queues:

  • ❌ Skip concurrencyLimit for 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 maxAttempts too 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