Construire des tâches de fond en production avec Trigger.dev v3 et Next.js

AI Bot
Par AI Bot ·

Chargement du lecteur de synthèse vocale...

Les applications web modernes doivent gérer des opérations qui ne devraient pas bloquer la requête utilisateur — envoyer des emails, traiter des images, synchroniser des données avec des API tierces, générer des rapports ou exécuter des pipelines IA. Sans système de tâches de fond, vous êtes coincé avec des solutions fragiles : des hacks setTimeout, des timeouts de fonctions edge, ou le déploiement de serveurs workers séparés.

Trigger.dev v3 résout ce problème en fournissant un framework de tâches de fond natif TypeScript qui exécute vos tâches sur une infrastructure gérée avec des retries intégrés, du logging et de la supervision. Vous écrivez des tâches comme de simples fonctions TypeScript, vous les déclenchez depuis vos routes API ou Server Actions Next.js, et Trigger.dev gère le reste — exécution, retries, planification et monitoring.

Dans ce tutoriel, vous allez construire un système d'automatisation d'onboarding utilisateur — quand un nouvel utilisateur s'inscrit, un workflow de fond envoie un email de bienvenue, génère un avatar personnalisé, synchronise l'utilisateur avec un CRM, et planifie un email de suivi pour 3 jours plus tard. En cours de route, vous maîtriserez les tâches Trigger.dev v3, la gestion des erreurs, les workflows multi-étapes, les tâches planifiées et le déploiement en production.

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • Node.js 20+ installé
  • Un compte Trigger.dev gratuit — inscrivez-vous sur le site Trigger.dev
  • Des connaissances de base en React et TypeScript
  • Une familiarité avec Next.js App Router (routes API, Server Actions)
  • Un éditeur de code (VS Code recommandé)

Ce que vous allez construire

Un système d'automatisation d'onboarding avec :

  • Tâche d'email de bienvenue — envoie un email formaté quand un utilisateur s'inscrit
  • Tâche de génération d'avatar — crée un avatar personnalisé via une API externe
  • Tâche de synchronisation CRM — pousse les données du nouvel utilisateur vers un CRM externe
  • Workflow d'onboarding multi-étapes — orchestre toutes les tâches en séquence avec gestion des erreurs
  • Suivi planifié — envoie un email de suivi 3 jours après l'inscription
  • Cron job — digest quotidien des nouvelles inscriptions pour l'équipe admin
  • Observabilité complète — logs, exécutions et erreurs visibles dans le dashboard Trigger.dev

Étape 1 : Créer le projet Next.js

Initialisez un nouveau projet Next.js 15 :

npx create-next-app@latest trigger-onboarding --typescript --tailwind --eslint --app --src-dir --use-npm
cd trigger-onboarding

Installez le SDK Trigger.dev et le CLI :

npm install @trigger.dev/sdk
npm install -D trigger.dev

Le package @trigger.dev/sdk fournit l'API runtime pour définir et déclencher des tâches. Le CLI trigger.dev gère le développement local et le déploiement.

Étape 2 : Initialiser Trigger.dev

Exécutez la commande init pour configurer Trigger.dev dans votre projet :

npx trigger.dev@latest init

Cette commande effectue plusieurs actions :

  1. Crée un fichier trigger.config.ts à la racine du projet
  2. Crée un répertoire src/trigger/ pour vos fichiers de tâches
  3. Ajoute la référence du projet et la clé API dans .env.local
  4. Met à jour package.json avec les scripts dev et deploy

Votre trigger.config.ts devrait ressembler à ceci :

import { defineConfig } from "@trigger.dev/sdk/v3";
 
export default defineConfig({
  project: "proj_your_project_id",
  runtime: "node",
  logLevel: "log",
  retries: {
    enabledInDev: true,
    default: {
      maxAttempts: 3,
      minTimeoutInMs: 1000,
      maxTimeoutInMs: 10000,
      factor: 2,
    },
  },
  dirs: ["src/trigger"],
});

La configuration retries est importante — elle définit une politique de retry par défaut pour toutes les tâches. Quand une tâche échoue, Trigger.dev la réessaie jusqu'à 3 fois avec un backoff exponentiel (délais de 1s, 2s, 4s).

Étape 3 : Créer votre première tâche — Email de bienvenue

Créez un fichier src/trigger/welcome-email.ts :

import { task, logger } from "@trigger.dev/sdk/v3";
 
export interface WelcomeEmailPayload {
  userId: string;
  email: string;
  name: string;
}
 
export const sendWelcomeEmail = task({
  id: "send-welcome-email",
  retry: {
    maxAttempts: 5,
  },
  run: async (payload: WelcomeEmailPayload) => {
    logger.info("Sending welcome email", {
      userId: payload.userId,
      email: payload.email,
    });
 
    const response = await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        from: "welcome@yourapp.com",
        to: payload.email,
        subject: `Welcome to Our App, ${payload.name}!`,
        html: `
          <h1>Welcome, ${payload.name}!</h1>
          <p>We are excited to have you on board.</p>
          <p>Here are some things you can do to get started:</p>
          <ul>
            <li>Complete your profile</li>
            <li>Explore the dashboard</li>
            <li>Connect your first integration</li>
          </ul>
        `,
      }),
    });
 
    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Failed to send email: ${error}`);
    }
 
    const result = await response.json();
 
    logger.info("Welcome email sent successfully", {
      emailId: result.id,
    });
 
    return { emailId: result.id, sentAt: new Date().toISOString() };
  },
});

Points clés à noter :

  • task() définit une tâche de fond avec un id unique
  • logger fournit un logging structuré visible dans le dashboard Trigger.dev
  • retry remplace la politique de retry par défaut — les emails sont critiques, donc on réessaie 5 fois
  • Lancer une erreur déclenche automatiquement un retry
  • La valeur de retour est stockée et visible dans le dashboard pour le débogage

Étape 4 : Créer la tâche de génération d'avatar

Créez src/trigger/generate-avatar.ts :

import { task, logger } from "@trigger.dev/sdk/v3";
 
export interface AvatarPayload {
  userId: string;
  name: string;
}
 
export const generateAvatar = task({
  id: "generate-avatar",
  retry: {
    maxAttempts: 3,
  },
  run: async (payload: AvatarPayload) => {
    logger.info("Generating avatar", { userId: payload.userId });
 
    const initials = payload.name
      .split(" ")
      .map((n) => n[0])
      .join("")
      .toUpperCase();
 
    const seed = encodeURIComponent(payload.name);
    const avatarUrl = `https://api.dicebear.com/8.x/initials/svg?seed=${seed}&chars=${initials.length}`;
 
    const response = await fetch(avatarUrl);
    if (!response.ok) {
      throw new Error(`Avatar generation failed: ${response.statusText}`);
    }
 
    const svgContent = await response.text();
 
    const storedUrl = `/avatars/${payload.userId}.svg`;
 
    logger.info("Avatar generated", {
      userId: payload.userId,
      url: storedUrl,
    });
 
    return { avatarUrl: storedUrl, svgSize: svgContent.length };
  },
});

Étape 5 : Créer la tâche de synchronisation CRM

Créez src/trigger/sync-crm.ts :

import { task, logger } from "@trigger.dev/sdk/v3";
 
export interface CrmSyncPayload {
  userId: string;
  email: string;
  name: string;
  signupDate: string;
}
 
export const syncToCrm = task({
  id: "sync-to-crm",
  retry: {
    maxAttempts: 4,
    minTimeoutInMs: 2000,
    maxTimeoutInMs: 30000,
    factor: 3,
  },
  run: async (payload: CrmSyncPayload) => {
    logger.info("Syncing user to CRM", {
      userId: payload.userId,
      email: payload.email,
    });
 
    const crmResponse = await fetch(
      `${process.env.CRM_API_URL}/contacts`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.CRM_API_KEY}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          email: payload.email,
          name: payload.name,
          properties: {
            signup_date: payload.signupDate,
            source: "web_app",
            lifecycle_stage: "subscriber",
          },
        }),
      }
    );
 
    if (!crmResponse.ok) {
      const error = await crmResponse.text();
      logger.error("CRM sync failed", { error });
      throw new Error(`CRM sync failed: ${error}`);
    }
 
    const contact = await crmResponse.json();
 
    logger.info("CRM sync completed", {
      crmContactId: contact.id,
    });
 
    return { crmContactId: contact.id };
  },
});

Notez la configuration de retry ici — les API CRM peuvent être instables, donc on utilise un backoff plus agressif (factor: 3) avec un timeout maximum de 30 secondes.

Étape 6 : Construire le workflow d'onboarding multi-étapes

Maintenant la partie puissante — orchestrer toutes les tâches en un seul workflow. Créez src/trigger/onboarding-workflow.ts :

import { task, logger, wait } from "@trigger.dev/sdk/v3";
import { sendWelcomeEmail } from "./welcome-email";
import { generateAvatar } from "./generate-avatar";
import { syncToCrm } from "./sync-crm";
 
export interface OnboardingPayload {
  userId: string;
  email: string;
  name: string;
}
 
export const onboardingWorkflow = task({
  id: "onboarding-workflow",
  retry: {
    maxAttempts: 1,
  },
  run: async (payload: OnboardingPayload) => {
    logger.info("Starting onboarding workflow", {
      userId: payload.userId,
    });
 
    // Étape 1 : Envoyer l'email de bienvenue et générer l'avatar en parallèle
    const [emailResult, avatarResult] = await Promise.all([
      sendWelcomeEmail.triggerAndWait({
        userId: payload.userId,
        email: payload.email,
        name: payload.name,
      }),
      generateAvatar.triggerAndWait({
        userId: payload.userId,
        name: payload.name,
      }),
    ]);
 
    logger.info("Email and avatar completed", {
      emailOk: emailResult.ok,
      avatarOk: avatarResult.ok,
    });
 
    // Étape 2 : Synchroniser avec le CRM
    const crmResult = await syncToCrm.triggerAndWait({
      userId: payload.userId,
      email: payload.email,
      name: payload.name,
      signupDate: new Date().toISOString(),
    });
 
    logger.info("CRM sync completed", { crmOk: crmResult.ok });
 
    // Étape 3 : Planifier un email de suivi dans 3 jours
    const followUpHandle = await sendFollowUpEmail.trigger(
      {
        userId: payload.userId,
        email: payload.email,
        name: payload.name,
      },
      {
        delay: "3d",
      }
    );
 
    logger.info("Follow-up email scheduled", {
      followUpId: followUpHandle.id,
    });
 
    return {
      emailResult: emailResult.ok ? emailResult.output : null,
      avatarResult: avatarResult.ok ? avatarResult.output : null,
      crmResult: crmResult.ok ? crmResult.output : null,
      followUpScheduled: followUpHandle.id,
    };
  },
});
 
const sendFollowUpEmail = task({
  id: "send-follow-up-email",
  retry: {
    maxAttempts: 5,
  },
  run: async (payload: OnboardingPayload) => {
    logger.info("Sending follow-up email", {
      userId: payload.userId,
    });
 
    const response = await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        from: "hello@yourapp.com",
        to: payload.email,
        subject: `How's it going, ${payload.name}?`,
        html: `
          <h1>Hey ${payload.name}!</h1>
          <p>You signed up 3 days ago — how is everything going?</p>
          <p>Need help getting started? Reply to this email and we will help!</p>
        `,
      }),
    });
 
    if (!response.ok) {
      throw new Error(`Follow-up email failed: ${await response.text()}`);
    }
 
    return { sent: true };
  },
});

Ce workflow démontre plusieurs fonctionnalités puissantes de Trigger.dev :

  • triggerAndWait() — déclenche une tâche enfant et attend son achèvement, retournant le résultat
  • Promise.all() — exécute des tâches indépendantes en parallèle pour de meilleures performances
  • trigger() avec delay — planifie une tâche pour le futur (3 jours plus tard)
  • Orchestration de workflow — une tâche parent coordonne plusieurs tâches enfants

Étape 7 : Ajouter un cron job planifié

Créez src/trigger/daily-digest.ts pour un digest admin quotidien :

import { schedules, logger } from "@trigger.dev/sdk/v3";
 
export const dailySignupDigest = schedules.task({
  id: "daily-signup-digest",
  cron: "0 9 * * *", // Chaque jour à 9h00 UTC
  run: async () => {
    logger.info("Running daily signup digest");
 
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1);
 
    const newUsers = await fetchNewUsers(yesterday);
 
    if (newUsers.length === 0) {
      logger.info("No new signups yesterday");
      return { sent: false, reason: "no_new_signups" };
    }
 
    const response = await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        from: "reports@yourapp.com",
        to: "admin@yourapp.com",
        subject: `Daily Signup Digest — ${newUsers.length} new users`,
        html: `
          <h1>Daily Signup Report</h1>
          <p>${newUsers.length} new users signed up yesterday:</p>
          <table>
            <tr><th>Name</th><th>Email</th><th>Time</th></tr>
            ${newUsers
              .map(
                (u) =>
                  `<tr><td>${u.name}</td><td>${u.email}</td><td>${u.createdAt}</td></tr>`
              )
              .join("")}
          </table>
        `,
      }),
    });
 
    if (!response.ok) {
      throw new Error(`Digest email failed: ${await response.text()}`);
    }
 
    logger.info("Digest sent", { userCount: newUsers.length });
    return { sent: true, userCount: newUsers.length };
  },
});
 
async function fetchNewUsers(since: Date) {
  return [
    {
      name: "Alice",
      email: "alice@example.com",
      createdAt: since.toISOString(),
    },
  ];
}

La fonction schedules.task() crée une tâche déclenchée par cron. L'expression cron 0 9 * * * signifie "chaque jour à 9h00 UTC".

Étape 8 : Déclencher les tâches depuis Next.js

Connectez maintenant le workflow à votre application Next.js. Créez une route API dans src/app/api/signup/route.ts :

import { NextRequest, NextResponse } from "next/server";
import { tasks } from "@trigger.dev/sdk/v3";
import type { onboardingWorkflow } from "@/trigger/onboarding-workflow";
 
export async function POST(request: NextRequest) {
  const body = await request.json();
  const { email, name, userId } = body;
 
  if (!email || !name || !userId) {
    return NextResponse.json(
      { error: "Missing required fields" },
      { status: 400 }
    );
  }
 
  const handle = await tasks.trigger<typeof onboardingWorkflow>(
    "onboarding-workflow",
    {
      userId,
      email,
      name,
    }
  );
 
  return NextResponse.json({
    message: "Signup successful! Onboarding started.",
    runId: handle.id,
  });
}

Point clé : tasks.trigger() retourne immédiatement — il n'attend pas que la tâche de fond se termine. L'utilisateur obtient une réponse instantanée pendant que le workflow d'onboarding s'exécute de manière asynchrone.

Vous pouvez également déclencher des tâches depuis les Server Actions. Créez src/app/actions.ts :

"use server";
 
import { tasks } from "@trigger.dev/sdk/v3";
import type { onboardingWorkflow } from "@/trigger/onboarding-workflow";
 
export async function signupAction(formData: FormData) {
  const email = formData.get("email") as string;
  const name = formData.get("name") as string;
  const userId = crypto.randomUUID();
 
  const handle = await tasks.trigger<typeof onboardingWorkflow>(
    "onboarding-workflow",
    { userId, email, name }
  );
 
  return { success: true, runId: handle.id };
}

Étape 9 : Construire un formulaire d'inscription simple

Créez src/app/page.tsx :

"use client";
 
import { useState } from "react";
import { signupAction } from "./actions";
 
export default function SignupPage() {
  const [status, setStatus] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
 
  async function handleSubmit(formData: FormData) {
    setLoading(true);
    try {
      const result = await signupAction(formData);
      setStatus(
        `Inscription réussie ! Onboarding en cours : ${result.runId}`
      );
    } catch (error) {
      setStatus("Une erreur est survenue. Veuillez réessayer.");
    } finally {
      setLoading(false);
    }
  }
 
  return (
    <main className="flex min-h-screen items-center justify-center bg-gray-50">
      <div className="w-full max-w-md rounded-lg bg-white p-8 shadow-md">
        <h1 className="mb-6 text-2xl font-bold text-gray-900">
          Inscription
        </h1>
        <form action={handleSubmit} className="space-y-4">
          <div>
            <label
              htmlFor="name"
              className="block text-sm font-medium text-gray-700"
            >
              Nom
            </label>
            <input
              type="text"
              id="name"
              name="name"
              required
              className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
            />
          </div>
          <div>
            <label
              htmlFor="email"
              className="block text-sm font-medium text-gray-700"
            >
              Email
            </label>
            <input
              type="email"
              id="email"
              name="email"
              required
              className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
            />
          </div>
          <button
            type="submit"
            disabled={loading}
            className="w-full rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
          >
            {loading ? "Inscription en cours..." : "S'inscrire"}
          </button>
        </form>
        {status && (
          <p className="mt-4 rounded bg-green-50 p-3 text-sm text-green-700">
            {status}
          </p>
        )}
      </div>
    </main>
  );
}

Étape 10 : Lancer le serveur de développement

Démarrez le serveur Next.js et le worker Trigger.dev :

# Terminal 1 : Next.js
npm run dev
 
# Terminal 2 : Worker Trigger.dev
npx trigger.dev@latest dev

La commande Trigger.dev dev se connecte au cloud Trigger.dev et exécute vos tâches localement. Ouvrez le dashboard Trigger.dev pour voir les logs en temps réel.

Testez le flux :

  1. Ouvrez votre app sur http://localhost:3000
  2. Remplissez le formulaire et soumettez
  3. Observez le dashboard Trigger.dev — vous verrez le workflow démarrer, les tâches email et avatar s'exécuter en parallèle, puis la synchro CRM, et enfin l'email de suivi planifié pour dans 3 jours

Étape 11 : Gestion des erreurs et stratégies de retry

Trigger.dev offre un contrôle granulaire sur la gestion des erreurs. Améliorons la tâche email :

import { task, logger, retry } from "@trigger.dev/sdk/v3";
 
export const sendWelcomeEmailV2 = task({
  id: "send-welcome-email-v2",
  retry: {
    maxAttempts: 5,
    minTimeoutInMs: 1000,
    maxTimeoutInMs: 60000,
    factor: 2,
  },
  onFailure: async (payload, error, params) => {
    logger.error("Welcome email permanently failed", {
      userId: payload.userId,
      error: error.message,
      attempts: params.attemptCount,
    });
 
    await fetch(process.env.SLACK_WEBHOOK_URL!, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        text: `Failed to send welcome email to ${payload.email} after ${params.attemptCount} attempts: ${error.message}`,
      }),
    });
  },
  run: async (payload: WelcomeEmailPayload) => {
    // Implémentation de la tâche...
  },
});

Le callback onFailure ne s'exécute qu'après épuisement de tous les retries — c'est votre dernière chance de gérer l'échec.

Vous pouvez aussi utiliser retry.onThrow pour un contrôle fin des retries dans une tâche :

run: async (payload) => {
  const result = await retry.onThrow(
    async () => {
      const res = await fetch("https://flaky-api.com/data");
      if (!res.ok) throw new Error("API call failed");
      return res.json();
    },
    { maxAttempts: 3, randomize: true }
  );
 
  logger.info("Got data", { result });
};

Étape 12 : Ajouter des métadonnées et des tags

Les tags et métadonnées rendent les tâches recherchables et filtrables dans le dashboard :

import { task, logger, metadata, tags } from "@trigger.dev/sdk/v3";
 
export const processOrder = task({
  id: "process-order",
  run: async (payload: { orderId: string; userId: string; amount: number }) => {
    tags.add("order", payload.orderId);
    tags.add("user", payload.userId);
 
    metadata.set("orderAmount", payload.amount);
    metadata.set("currency", "USD");
    metadata.set("status", "processing");
 
    // ... traitement de la commande ...
 
    metadata.set("status", "completed");
 
    return { processed: true };
  },
});

Étape 13 : Opérations par lots avec batchTrigger

Quand vous devez traiter plusieurs éléments, utilisez le déclenchement par lots :

import { tasks } from "@trigger.dev/sdk/v3";
import type { sendWelcomeEmail } from "@/trigger/welcome-email";
 
export async function sendBulkEmails(
  users: Array<{ userId: string; email: string; name: string }>
) {
  const handle = await tasks.batchTrigger<typeof sendWelcomeEmail>(
    "send-welcome-email",
    users.map((user) => ({
      payload: user,
    }))
  );
 
  return {
    batchId: handle.batchId,
    runCount: handle.runs.length,
  };
}

Trigger.dev traite les éléments par lots avec une concurrence contrôlée, évitant de surcharger les services en aval.

Étape 14 : Variables d'environnement et configuration

Ajoutez vos variables dans .env.local :

# Trigger.dev
TRIGGER_SECRET_KEY=tr_dev_your_secret_key
 
# Email (Resend)
RESEND_API_KEY=re_your_api_key
 
# CRM
CRM_API_URL=https://api.your-crm.com
CRM_API_KEY=your_crm_key
 
# Notifications
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/webhook

Pour la production, ajoutez ces mêmes variables dans le dashboard Trigger.dev sous les paramètres du projet.

Étape 15 : Déployer en production

Déployez vos tâches sur le cloud Trigger.dev :

npx trigger.dev@latest deploy

Cette commande :

  1. Bundle votre code de tâches
  2. Le téléverse sur le cloud Trigger.dev
  3. Rend vos tâches disponibles pour le déclenchement en production

Pour l'intégration CI/CD, ajoutez la commande deploy à votre pipeline :

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]
 
jobs:
  deploy-trigger:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx trigger.dev@latest deploy
        env:
          TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }}

Étape 16 : Surveiller et déboguer

Le dashboard Trigger.dev offre une observabilité complète :

  • Liste des exécutions — voir toutes les exécutions de tâches avec statut, durée et horodatage
  • Détails d'exécution — voir les logs, le payload, la sortie et l'historique des retries
  • Trace du workflow — visualiser les workflows multi-étapes avec les relations parent-enfant
  • Erreurs — filtrer et rechercher les exécutions échouées avec les stack traces complètes
  • Planifications — gérer et surveiller les cron jobs

Tester votre implémentation

Pour vérifier que tout fonctionne :

  1. Démarrez le serveur dev et le worker Trigger.dev dans deux terminaux
  2. Soumettez le formulaire — vous devriez voir une réponse instantanée
  3. Vérifiez le dashboard Trigger.dev — le workflow devrait montrer toutes les étapes
  4. Testez la gestion des erreurs — cassez temporairement une clé API et vérifiez les retries
  5. Vérifiez le cron job — il devrait apparaître dans l'onglet Planifications

Dépannage

Les tâches n'apparaissent pas dans le dashboard : Assurez-vous que npx trigger.dev@latest dev est en cours d'exécution. Vérifiez que trigger.config.ts pointe vers les bons répertoires.

Les tâches échouent avec "Module not found" : Trigger.dev bundle les tâches séparément de Next.js. Assurez-vous que tous les imports dans les fichiers de tâches sont autonomes.

Les retries ne fonctionnent pas en développement : Configurez enabledInDev: true dans la configuration retries de trigger.config.ts.

Les variables d'environnement ne sont pas disponibles : En développement, les tâches lisent depuis .env.local. En production, configurez les variables dans le dashboard Trigger.dev.

Structure du projet

Voici la structure finale du projet :

trigger-onboarding/
  src/
    app/
      api/
        signup/
          route.ts          # Route API pour déclencher l'onboarding
      actions.ts            # Server Action pour la soumission du formulaire
      page.tsx              # Interface du formulaire d'inscription
    trigger/
      welcome-email.ts      # Tâche d'email de bienvenue
      generate-avatar.ts    # Tâche de génération d'avatar
      sync-crm.ts           # Tâche de synchronisation CRM
      onboarding-workflow.ts # Orchestrateur de workflow multi-étapes
      daily-digest.ts       # Cron job planifié
  trigger.config.ts         # Configuration Trigger.dev
  .env.local                # Variables d'environnement

Prochaines étapes

Maintenant que vous avez un système de tâches de fond fonctionnel, envisagez :

  • Ajouter plus de workflows — traitement de commandes, génération de rapports, pipelines de données
  • Implémenter l'idempotence — utilisez des clés uniques pour éviter les exécutions en double
  • Configurer les alertes — notifications Slack ou email pour les tâches échouées
  • Utiliser Trigger.dev avec une base de données — combinez avec Prisma ou Drizzle
  • Explorer les patterns de fan-out — utilisez batchTriggerAndWait pour traiter de grands datasets
  • Auto-hébergement — Trigger.dev v3 peut être auto-hébergé si nécessaire

Conclusion

Trigger.dev v3 apporte le traitement des tâches de fond à l'écosystème TypeScript avec une expérience développeur qui semble native à Next.js. Au lieu de gérer des files Redis, des processus workers et de la logique de retry vous-même, vous écrivez de simples fonctions TypeScript et Trigger.dev gère l'infrastructure.

Dans ce tutoriel, vous avez construit un système complet d'automatisation d'onboarding avec exécution parallèle de tâches, workflows multi-étapes, cron jobs planifiés, gestion des erreurs avec retries, et déploiement en production. Les mêmes patterns s'appliquent à tout besoin de traitement de fond — de l'envoi d'emails au traitement de paiements en passant par l'exécution de pipelines IA.

Les points clés à retenir :

  1. Les tâches sont de simples fonctions TypeScript — pas de syntaxe spéciale à apprendre
  2. triggerAndWait permet l'orchestration de workflows — composez des workflows complexes à partir de tâches simples
  3. Les retries et la gestion des erreurs sont intégrés — configurez une fois, gérez les échecs avec élégance
  4. Les tâches planifiées remplacent les serveurs cron — pas d'infrastructure à gérer
  5. Le dashboard offre une observabilité complète — déboguez les problèmes en production avec logs, traces et métadonnées

Vous voulez lire plus de tutoriels? Découvrez notre dernier tutoriel sur Construire un Chatbot IA Local avec Ollama et Next.js : Guide Complet.

Discutez de votre projet avec nous

Nous sommes ici pour vous aider avec vos besoins en développement Web. Planifiez un appel pour discuter de votre projet et comment nous pouvons vous aider.

Trouvons les meilleures solutions pour vos besoins.

Articles connexes

Construire un Agent IA Autonome avec Agentic RAG et Next.js

Apprenez a construire un agent IA qui decide de maniere autonome quand et comment recuperer des informations depuis des bases de donnees vectorielles. Un guide pratique complet avec Vercel AI SDK et Next.js, accompagne d'exemples executables.

30 min read·