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
- Utilisez toujours
@trigger.dev/sdk— n'utilisez jamais le deprecatedclient.defineJob - Vérifiez
result.okavant d'accéder àresult.outputdepuistriggerAndWait() - N'utilisez jamais
Promise.allavectriggerAndWait()ou les appelswait.* - 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
- Rendez les tâches idempotentes — sûres à relancer sans effets secondaires
- Utilisez les files d'attente pour éviter de surcharger les services externes
- Configurez les tentatives appropriées avec backoff exponentiel
- Suivez la progression avec les métadonnées pour les tâches longue durée
- Utilisez le debouncing pour l'activité utilisateur et les rafales de webhooks
- Adaptez la taille de la machine aux exigences de calcul
Voir references/ pour la documentation détaillée sur chaque fonctionnalité.