Skip to content
cccskills
FreePublished on GitHub

trigger-advanced-tasks

Apply advanced Trigger.dev task patterns for concurrency, retries, idempotency, progress tracking, machine sizing, and run management. The guidance helps structure resilient tasks and monitor execution details, including cost, duration, logs, and child task progress.

OriginalShow the original description

Comprehensive rules to help you write advanced Trigger.dev tasks

trycompai/comp2,061 stars(GitHub 2,061 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Limiting task concurrency with queues
  • Configuring retries and fatal error handling
  • Preventing duplicate payment or critical operations
  • Tracking progress in a long-running task
  • Monitoring run cost and duration

Files included (1)

  • SKILL.md11.4 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Source Cursor rule: .cursor/rules/trigger.advanced-tasks.mdc. Original file scope: **/trigger/**/*.ts. Original Cursor alwaysApply: false.

Trigger.dev Advanced Tasks (v4)

Advanced patterns and features for writing tasks

Tags & Organization

import { task, tags } from '@trigger.dev/sdk';

export const processUser = task({
  id: 'process-user',
  run: async (payload: { userId: string; orgId: string }, { ctx }) => {
    // Add tags during execution
    await tags.add(`user_${payload.userId}`);
    await tags.add(`org_${payload.orgId}`);

    return { processed: true };
  },
});

// Trigger with tags
await processUser.trigger(
  { userId: '123', orgId: 'abc' },
  { tags: ['priority', 'user_123', 'org_abc'] }, // Max 10 tags per run
);

// Subscribe to tagged runs
for await (const run of runs.subscribeToRunsWithTag('user_123')) {
  console.log(`User task ${run.id}: ${run.status}`);
}

Tag Best Practices:

  • Use prefixes: user_123, org_abc, video:456
  • Max 10 tags per run, 1-128 characters each
  • Tags don't propagate to child tasks automatically

Concurrency & Queues

import { task, queue } from '@trigger.dev/sdk';

// Shared queue for related tasks
const emailQueue = queue({
  name: 'email-processing',
  concurrencyLimit: 5, // Max 5 emails processing simultaneously
});

// Task-level concurrency
export const oneAtATime = task({
  id: 'sequential-task',
  queue: { concurrencyLimit: 1 }, // Process one at a time
  run: async (payload) => {
    // Critical section - only one instance runs
  },
});

// Per-user concurrency
export const processUserData = task({
  id: 'process-user-data',
  run: async (payload: { userId: string }) => {
    // Override queue with user-specific concurrency
    await childTask.trigger(payload, {
      queue: {
        name: `user-${payload.userId}`,
        concurrencyLimit: 2,
      },
    });
  },
});

export const emailTask = task({
  id: 'send-email',
  queue: emailQueue, // Use shared queue
  run: async (payload: { to: string }) => {
    // Send email logic
  },
});

Error Handling & Retries

import { task, retry, AbortTaskRunError } from '@trigger.dev/sdk';

export const resilientTask = task({
  id: 'resilient-task',
  retry: {
    maxAttempts: 10,
    factor: 1.8, // Exponential backoff multiplier
    minTimeoutInMs: 500,
    maxTimeoutInMs: 30_000,
    randomize: false,
  },
  catchError: async ({ error, ctx }) => {
    // Custom error handling
    if (error.code === 'FATAL_ERROR') {
      throw new AbortTaskRunError('Cannot retry this error');
    }

    // Log error details
    console.error(`Task ${ctx.task.id} failed:`, error);

    // Allow retry by returning nothing
    return { retryAt: new Date(Date.now() + 60000) }; // Retry in 1 minute
  },
  run: async (payload) => {
    // Retry specific operations
    const result = await retry.onThrow(
      async () => {
        return await unstableApiCall(payload);
      },
      { maxAttempts: 3 },
    );

    // Conditional HTTP retries
    const response = await retry.fetch('https://api.example.com', {
      retry: {
        maxAttempts: 5,
        condition: (response, error) => {
          return response?.status === 429 || response?.status >= 500;
        },
      },
    });

    return result;
  },
});

Machines & Performance

export const heavyTask = task({
  id: 'heavy-computation',
  machine: { preset: 'large-2x' }, // 8 vCPU, 16 GB RAM
  maxDuration: 1800, // 30 minutes timeout
  run: async (payload, { ctx }) => {
    // Resource-intensive computation
    if (ctx.machine.preset === 'large-2x') {
      // Use all available cores
      return await parallelProcessing(payload);
    }

    return await standardProcessing(payload);
  },
});

// Override machine when triggering
await heavyTask.trigger(payload, {
  machine: { preset: 'medium-1x' }, // Override for this run
});

Machine Presets:

  • micro: 0.25 vCPU, 0.25 GB RAM
  • small-1x: 0.5 vCPU, 0.5 GB RAM (default)
  • small-2x: 1 vCPU, 1 GB RAM
  • medium-1x: 1 vCPU, 2 GB RAM
  • medium-2x: 2 vCPU, 4 GB RAM
  • large-1x: 4 vCPU, 8 GB RAM
  • large-2x: 8 vCPU, 16 GB RAM

Idempotency

import { task, idempotencyKeys } from '@trigger.dev/sdk';

export const paymentTask = task({
  id: 'process-payment',
  retry: {
    maxAttempts: 3,
  },
  run: async (payload: { orderId: string; amount: number }) => {
    // Automatically scoped to this task run, so if the task is retried, the idempotency key will be the same
    const idempotencyKey = await idempotencyKeys.create(`payment-${payload.orderId}`);

    // Ensure payment is processed only once
    await chargeCustomer.trigger(payload, {
      idempotencyKey,
      idempotencyKeyTTL: '24h', // Key expires in 24 hours
    });
  },
});

// Payload-based idempotency
import { createHash } from 'node:crypto';

function createPayloadHash(payload: any): string {
  const hash = createHash('sha256');
  hash.update(JSON.stringify(payload));
  return hash.digest('hex');
}

export const deduplicatedTask = task({
  id: 'deduplicated-task',
  run: async (payload) => {
    const payloadHash = createPayloadHash(payload);
    const idempotencyKey = await idempotencyKeys.create(payloadHash);

    await processData.trigger(payload, { idempotencyKey });
  },
});

Metadata & Progress Tracking

import { task, metadata } from '@trigger.dev/sdk';

export const batchProcessor = task({
  id: 'batch-processor',
  run: async (payload: { items: any[] }, { ctx }) => {
    const totalItems = payload.items.length;

    // Initialize progress metadata
    metadata
      .set('progress', 0)
      .set('totalItems', totalItems)
      .set('processedItems', 0)
      .set('status', 'starting');

    const results = [];

    for (let i = 0; i < payload.items.length; i++) {
      const item = payload.items[i];

      // Process item
      const result = await processItem(item);
      results.push(result);

      // Update progress
      const progress = ((i + 1) / totalItems) * 100;
      metadata
        .set('progress', progress)
        .increment('processedItems', 1)
        .append('logs', `Processed item ${i + 1}/${totalItems}`)
        .set('currentItem', item.id);
    }

    // Final status
    metadata.set('status', 'completed');

    return { results, totalProcessed: results.length };
  },
});

// Update parent metadata from child task
export const childTask = task({
  id: 'child-task',
  run: async (payload, { ctx }) => {
    // Update parent task metadata
    metadata.parent.set('childStatus', 'processing');
    metadata.root.increment('childrenCompleted', 1);

    return { processed: true };
  },
});

Advanced Triggering

Frontend Triggering (React)

'use client';
import { useTaskTrigger } from '@trigger.dev/react-hooks';
import type { myTask } from '../trigger/tasks';

function TriggerButton({ accessToken }: { accessToken: string }) {
  const { submit, handle, isLoading } = useTaskTrigger<typeof myTask>('my-task', { accessToken });

  return (
    <button onClick={() => submit({ data: 'from frontend' })} disabled={isLoading}>
      Trigger Task
    </button>
  );
}

Large Payloads

// For payloads > 512KB (max 10MB)
export const largeDataTask = task({
  id: 'large-data-task',
  run: async (payload: { dataUrl: string }) => {
    // Trigger.dev automatically handles large payloads
    // For > 10MB, use external storage
    const response = await fetch(payload.dataUrl);
    const largeData = await response.json();

    return { processed: largeData.length };
  },
});

// Best practice: Use presigned URLs for very large files
await largeDataTask.trigger({
  dataUrl: 'https://s3.amazonaws.com/bucket/large-file.json?presigned=true',
});

Advanced Options

await myTask.trigger(payload, {
  delay: '2h30m', // Delay execution
  ttl: '24h', // Expire if not started within 24 hours
  priority: 100, // Higher priority (time offset in seconds)
  tags: ['urgent', 'user_123'],
  metadata: { source: 'api', version: 'v2' },
  queue: {
    name: 'priority-queue',
    concurrencyLimit: 10,
  },
  idempotencyKey: 'unique-operation-id',
  idempotencyKeyTTL: '1h',
  machine: { preset: 'large-1x' },
  maxAttempts: 5,
});

Hidden Tasks

// Hidden task - not exported, only used internally
const internalProcessor = task({
  id: 'internal-processor',
  run: async (payload: { data: string }) => {
    return { processed: payload.data.toUpperCase() };
  },
});

// Public task that uses hidden task
export const publicWorkflow = task({
  id: 'public-workflow',
  run: async (payload: { input: string }) => {
    // Use hidden task internally
    const result = await internalProcessor.triggerAndWait({
      data: payload.input,
    });

    if (result.ok) {
      return { output: result.output.processed };
    }

    throw new Error('Internal processing failed');
  },
});

Logging & Tracing

import { task, logger } from '@trigger.dev/sdk';

export const tracedTask = task({
  id: 'traced-task',
  run: async (payload, { ctx }) => {
    logger.info('Task started', { userId: payload.userId });

    // Custom trace with attributes
    const user = await logger.trace(
      'fetch-user',
      async (span) => {
        span.setAttribute('user.id', payload.userId);
        span.setAttribute('operation', 'database-fetch');

        const userData = await database.findUser(payload.userId);
        span.setAttribute('user.found', !!userData);

        return userData;
      },
      { userId: payload.userId },
    );

    logger.debug('User fetched', { user: user.id });

    try {
      const result = await processUser(user);
      logger.info('Processing completed', { result });
      return result;
    } catch (error) {
      logger.error('Processing failed', {
        error: error.message,
        userId: payload.userId,
      });
      throw error;
    }
  },
});

Usage Monitoring

import { logger, task, usage } from '@trigger.dev/sdk';

export const monitoredTask = task({
  id: 'monitored-task',
  run: async (payload) => {
    // Get current run cost
    const currentUsage = await usage.getCurrent();
    logger.info('Current cost', {
      costInCents: currentUsage.costInCents,
      durationMs: currentUsage.durationMs,
    });

    // Measure specific operation
    const { result, compute } = await usage.measure(async () => {
      return await expensiveOperation(payload);
    });

    logger.info('Operation cost', {
      costInCents: compute.costInCents,
      durationMs: compute.durationMs,
    });

    return result;
  },
});

Run Management

// Cancel runs
await runs.cancel('run_123');

// Replay runs with same payload
await runs.replay('run_123');

// Retrieve run with cost details
const run = await runs.retrieve('run_123');
console.log(`Cost: ${run.costInCents} cents, Duration: ${run.durationMs}ms`);

Best Practices

  • Concurrency: Use queues to prevent overwhelming external services
  • Retries: Configure exponential backoff for transient failures
  • Idempotency: Always use for payment/critical operations
  • Metadata: Track progress for long-running tasks
  • Machines: Match machine size to computational requirements
  • Tags: Use consistent naming patterns for filtering
  • Large Payloads: Use external storage for files > 10MB
  • Error Handling: Distinguish between retryable and fatal errors

Design tasks to be stateless, idempotent, and resilient to failures. Use metadata for state tracking and queues for resource management.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Guide API endpoint changes so request DTOs, runtime validation, OpenAPI documentation, and MCP tool metadata stay aligned. It covers discoverability, authentication, uploads, long-running tasks, and response types that MCP tools cannot handle.

  • Adding an API endpoint with a request body
  • Updating DTO validation and OpenAPI metadata
  • Making an endpoint available as an MCP tool
trycompai/comp2.1KUpdated Oct 24, 2026

Review specified files for design system usage, replace legacy @trycompai/ui components and lucide-react icons with available @trycompai/design-system equivalents, and run the app type check to verify the migration.

  • Migrating legacy UI components
  • Replacing lucide-react icons with Carbon icons
  • Checking specified files for design system compliance
trycompai/comp2.1KUpdated Oct 24, 2026

audit-hooks

FreeSummary

Review specified files for outdated hook and API patterns, then replace them with the prescribed SWR hooks, API clients, or server API calls. The guidance covers data fetching, mutations, cache updates, and response handling, followed by a typecheck command.

  • Auditing files for disallowed API and hook patterns
  • Replacing raw fetch calls with the API client
  • Migrating server actions and database mutations to API hooks
trycompai/comp2.1KUpdated Oct 24, 2026

audit-rbac

FreeSummary

Review NestJS API endpoints and frontend components for role-based access control and audit log compliance. The skill lists permission, guard, route, and UI gating rules, then directs the agent to fix violations and run type checks.

  • Auditing API routes for permission decorators
  • Checking UI actions against user permissions
  • Fixing access control inconsistencies
trycompai/comp2.1KUpdated Oct 24, 2026

audit-tests

FreeSummary

Audit unit tests for components that use permissions. Add missing tests and fix failures, checking that administrators can use mutation controls, auditors cannot, and read-only content remains visible to both.

  • Adding permission tests for a component
  • Checking admin and auditor behavior
  • Fixing failing permission-related tests
trycompai/comp2.1KUpdated Oct 24, 2026

Set up Better Auth for TypeScript applications, including server and client configuration, database adapters, sessions, email and social sign-in, and plugins. The guide also highlights environment variables, schema generation, and security settings to review.

  • Configuring Better Auth in a TypeScript app
  • Connecting an ORM or database adapter
  • Setting up sessions and cookie storage
trycompai/comp2.1KUpdated Oct 24, 2026

See all skills by trycompai

Report a problem with this skill