trigger-tasks

Par triggerdotdev · skills

Créez des agents IA, des workflows et des tâches d'arrière-plan durables avec Trigger.dev. À utiliser pour créer des tâches, déclencher des jobs, gérer les nouvelles tentatives, planifier des cron jobs, ou implémenter des files d'attente et le contrôle de la concurrence.

npx skills add https://github.com/triggerdotdev/skills --skill trigger-tasks

Tâches Trigger.dev

Construisez des tâches de fond durables qui s'exécutent de manière fiable avec des tentatives automatiques, la mise en file d'attente et l'observabilité.

Quand utiliser

  • Créer des jobs de fond ou des workflows asynchrones
  • Construire des agents IA qui nécessitent une exécution longue durée
  • Traiter les webhooks, emails ou téléchargements de fichiers
  • Planifier des tâches récurrentes (cron)
  • Tout travail qui ne devrait pas bloquer votre application principale

Règles critiques

  1. Utilisez toujours @trigger.dev/sdk — n'utilisez jamais le deprecated client.defineJob
  2. Vérifiez result.ok avant d'accéder à result.output depuis triggerAndWait()
  3. N'utilisez jamais Promise.all avec triggerAndWait() ou les appels wait.*
  4. Exportez les tâches depuis les fichiers de votre répertoire trigger/

Tâche basique

import { task } from "@trigger.dev/sdk";

export const processData = task({
  id: "process-data",
  retry: {
    maxAttempts: 10,
    factor: 1.8,
    minTimeoutInMs: 500,
    maxTimeoutInMs: 30_000,
  },
  run: async (payload: { userId: string; data: any[] }) => {
    console.log(`Processing ${payload.data.length} items`);
    return { processed: payload.data.length };
  },
});

Tâche avec schéma (entrée validée)

import { schemaTask } from "@trigger.dev/sdk";
import { z } from "zod";

export const validatedTask = schemaTask({
  id: "validated-task",
  schema: z.object({
    name: z.string(),
    email: z.string().email(),
  }),
  run: async (payload) => {
    // payload est typé et validé
    return { message: `Hello ${payload.name}` };
  },
});

Déclencher des tâches

Depuis le code backend

import { tasks } from "@trigger.dev/sdk";
import type { processData } from "./trigger/tasks";

// Déclenchement simple (fire and forget)
const handle = await tasks.trigger<typeof processData>("process-data", {
  userId: "123",
  data: [{ id: 1 }],
});

// Déclenchement par batch (jusqu'à 1 000 éléments, 3 MB par payload)
const batchHandle = await tasks.batchTrigger<typeof processData>("process-data", [
  { payload: { userId: "123", data: [] } },
  { payload: { userId: "456", data: [] } },
]);

Depuis l'intérieur des tâches

export const parentTask = task({
  id: "parent-task",
  run: async (payload) => {
    // Fire and forget
    const handle = await childTask.trigger({ data: "value" });

    // Attendre le résultat - retourne un objet Result, PAS la sortie directe
    const result = await childTask.triggerAndWait({ data: "value" });
    if (result.ok) {
      console.log("Output:", result.output);
    } else {
      console.error("Failed:", result.error);
    }

    // Unwrap rapide (lève une erreur en cas d'échec)
    const output = await childTask.triggerAndWait({ data: "value" }).unwrap();

    // Batch avec attente
    const results = await childTask.batchTriggerAndWait([
      { payload: { data: "item1" } },
      { payload: { data: "item2" } },
    ]);
  },
});

Attentes

import { task, wait } from "@trigger.dev/sdk";

export const taskWithWaits = task({
  id: "task-with-waits",
  run: async (payload) => {
    await wait.for({ seconds: 30 });
    await wait.for({ minutes: 5 });
    await wait.until({ date: new Date("2024-12-25") });

    // Attendre l'approbation externe
    await wait.forToken({
      token: "user-approval-token",
      timeoutInSeconds: 3600,
    });
  },
});

Les attentes > 5 secondes sont checkpointées et ne comptent pas vers le calcul.

Concurrence et files d'attente

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

// File d'attente partagée
const emailQueue = queue({
  name: "email-processing",
  concurrencyLimit: 5,
});

// Concurrence au niveau de la tâche
export const oneAtATime = task({
  id: "sequential-task",
  queue: { concurrencyLimit: 1 },
  run: async (payload) => {
    // Une seule instance s'exécute à la fois
  },
});

// Utiliser une file d'attente partagée
export const emailTask = task({
  id: "send-email",
  queue: emailQueue,
  run: async (payload) => {},
});

// Concurrence par locataire (au moment du déclenchement)
await childTask.trigger(payload, {
  queue: {
    name: `user-${userId}`,
    concurrencyLimit: 2,
  },
});

Debouncing

Consolidez les déclenchements rapides en une seule exécution :

await myTask.trigger(
  { userId: "123" },
  {
    debounce: {
      key: "user-123-update",
      delay: "5s",
      mode: "trailing", // Utiliser le dernier payload (défaut : "leading")
    },
  }
);

Idempotence

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

export const paymentTask = task({
  id: "process-payment",
  run: async (payload: { orderId: string }) => {
    const key = await idempotencyKeys.create(`payment-${payload.orderId}`);

    await chargeCustomer.trigger(payload, {
      idempotencyKey: key,
      idempotencyKeyTTL: "24h",
    });
  },
});

Gestion des erreurs et tentatives

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

export const resilientTask = task({
  id: "resilient-task",
  retry: {
    maxAttempts: 10,
    factor: 1.8,
    minTimeoutInMs: 500,
    maxTimeoutInMs: 30_000,
  },
  catchError: async ({ error, ctx }) => {
    if (error.code === "FATAL_ERROR") {
      throw new AbortTaskRunError("Cannot retry");
    }
    return { retryAt: new Date(Date.now() + 60000) };
  },
  run: async (payload) => {
    // Réessayer des opérations spécifiques
    const result = await retry.onThrow(
      async () => unstableApiCall(payload),
      { maxAttempts: 3 }
    );

    // Tentatives HTTP avec conditions
    const response = await retry.fetch("https://api.example.com", {
      retry: {
        maxAttempts: 5,
        condition: (res, err) => res?.status === 429 || res?.status >= 500,
      },
    });
  },
});

Tâches planifiées (Cron)

import { schedules } from "@trigger.dev/sdk";

// Calendrier déclaratif
export const dailyTask = schedules.task({
  id: "daily-cleanup",
  cron: "0 0 * * *", // Minuit UTC
  run: async (payload) => {
    // payload.timestamp - heure planifiée
    // payload.timezone - fuseau horaire IANA
    // payload.scheduleId - identifiant de la planification
  },
});

// Avec fuseau horaire
export const tokyoTask = schedules.task({
  id: "tokyo-morning",
  cron: { pattern: "0 9 * * *", timezone: "Asia/Tokyo" },
  run: async () => {},
});

// Planifications dynamiques/multi-locataires
await schedules.create({
  task: "reminder-task",
  cron: "0 8 * * *",
  timezone: "America/New_York",
  externalId: userId,
  deduplicationKey: `${userId}-daily`,
});

Métadonnées et progression

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

export const batchProcessor = task({
  id: "batch-processor",
  run: async (payload: { items: any[] }) => {
    metadata.set("progress", 0).set("total", payload.items.length);

    for (let i = 0; i < payload.items.length; i++) {
      await processItem(payload.items[i]);
      metadata.set("progress", ((i + 1) / payload.items.length) * 100);
    }

    metadata.set("status", "completed");
  },
});

Tags

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

export const processUser = task({
  id: "process-user",
  run: async (payload: { userId: string }) => {
    await tags.add(`user_${payload.userId}`);
  },
});

// Déclencher avec tags
await processUser.trigger(
  { userId: "123" },
  { tags: ["priority", "user_123"] }
);

Présets de machine

export const heavyTask = task({
  id: "heavy-computation",
  machine: { preset: "large-2x" }, // 8 vCPU, 16 GB RAM
  maxDuration: 1800, // 30 minutes
  run: async (payload) => {},
});
Preset vCPU RAM
micro 0,25 0,25 GB
small-1x 0,5 0,5 GB (défaut)
small-2x 1 1 GB
medium-1x 1 2 GB
medium-2x 2 4 GB
large-1x 4 8 GB
large-2x 8 16 GB

Bonnes pratiques

  1. Rendez les tâches idempotentes — sûres à relancer sans effets secondaires
  2. Utilisez les files d'attente pour éviter de surcharger les services externes
  3. Configurez les tentatives appropriées avec backoff exponentiel
  4. Suivez la progression avec les métadonnées pour les tâches longue durée
  5. Utilisez le debouncing pour l'activité utilisateur et les rafales de webhooks
  6. Adaptez la taille de la machine aux exigences de calcul

Voir references/ pour la documentation détaillée sur chaque fonctionnalité.

Skills similaires