بناء مهام خلفية للإنتاج باستخدام Trigger.dev v3 و Next.js

AI Bot
بواسطة AI Bot ·

جاري تحميل مشغل تحويل النص إلى كلام الصوتي...

تحتاج تطبيقات الويب الحديثة إلى التعامل مع عمليات لا ينبغي أن تحجب طلب المستخدم — إرسال رسائل البريد الإلكتروني، معالجة الصور، مزامنة البيانات مع واجهات برمجية خارجية، إنشاء التقارير، أو تشغيل خطوط أنابيب الذكاء الاصطناعي. بدون نظام مهام خلفية، ستضطر للجوء إلى حلول هشة: حيل setTimeout، انتهاء مهلة دوال الحافة، أو تشغيل خادم عمال منفصل.

يحل Trigger.dev v3 هذه المشكلة بتوفير إطار عمل مهام خلفية أصلي بـ TypeScript يعمل على بنية تحتية مُدارة مع إعادة محاولة مدمجة وتسجيل ومراقبة. تكتب المهام كدوال TypeScript عادية، تُطلقها من مسارات API أو Server Actions في Next.js، ويتولى Trigger.dev الباقي — التنفيذ وإعادة المحاولة والجدولة والمراقبة.

في هذا الدرس، ستبني نظام أتمتة إعداد المستخدمين — عندما يسجل مستخدم جديد، يرسل سير عمل خلفي بريد ترحيب، وينشئ صورة رمزية مخصصة، ويزامن المستخدم مع نظام إدارة علاقات العملاء (CRM)، ويجدول بريد متابعة بعد 3 أيام. خلال ذلك، ستتقن مهام Trigger.dev v3 ومعالجة الأخطاء وسير العمل متعدد الخطوات والمهام المجدولة والنشر في الإنتاج.

المتطلبات الأساسية

قبل البدء، تأكد من توفر:

  • Node.js 20+ مثبت
  • حساب مجاني على Trigger.dev — سجّل على موقع Trigger.dev
  • معرفة أساسية بـ React و TypeScript
  • إلمام بـ Next.js App Router (مسارات API و Server Actions)
  • محرر أكواد (يُنصح بـ VS Code)

ما ستبنيه

نظام أتمتة إعداد المستخدمين يتضمن:

  • مهمة بريد الترحيب — ترسل بريدًا منسقًا عند تسجيل مستخدم
  • مهمة إنشاء الصورة الرمزية — تنشئ صورة رمزية مخصصة باستخدام واجهة برمجية خارجية
  • مهمة مزامنة CRM — تدفع بيانات المستخدم الجديد إلى نظام CRM خارجي
  • سير عمل إعداد متعدد الخطوات — ينسق جميع المهام بالتسلسل مع معالجة الأخطاء
  • متابعة مجدولة — ترسل بريد متابعة بعد 3 أيام من التسجيل
  • مهمة Cron — ملخص يومي بالتسجيلات الجديدة لفريق الإدارة
  • مراقبة كاملة — السجلات والتشغيلات والأخطاء مرئية في لوحة تحكم Trigger.dev

الخطوة 1: إنشاء مشروع Next.js

أنشئ مشروع Next.js 15 جديد:

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

ثبّت حزمة Trigger.dev SDK وأداة سطر الأوامر:

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

حزمة @trigger.dev/sdk توفر واجهة برمجة وقت التشغيل لتعريف المهام وإطلاقها. أداة سطر الأوامر trigger.dev تتعامل مع التطوير المحلي والنشر.

الخطوة 2: تهيئة Trigger.dev

شغّل أمر التهيئة لإعداد Trigger.dev في مشروعك:

npx trigger.dev@latest init

يقوم هذا الأمر بعدة أشياء:

  1. ينشئ ملف trigger.config.ts في جذر المشروع
  2. ينشئ مجلد src/trigger/ لملفات المهام
  3. يضيف مرجع المشروع ومفتاح API إلى .env.local
  4. يحدّث package.json بسكريبتات التطوير والنشر

يجب أن يبدو ملف trigger.config.ts هكذا:

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"],
});

إعدادات retries مهمة — تحدد سياسة إعادة المحاولة الافتراضية لجميع المهام. عندما تفشل مهمة، يعيد Trigger.dev محاولتها حتى 3 مرات مع تأخير تصاعدي أسي (1 ثانية، 2 ثانية، 4 ثوانٍ).

الخطوة 3: إنشاء أول مهمة — بريد الترحيب

أنشئ ملف 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() };
  },
});

أشياء مهمة يجب ملاحظتها:

  • task() يعرّف مهمة خلفية بمعرّف فريد id
  • logger يوفر تسجيلًا منظمًا مرئيًا في لوحة تحكم Trigger.dev
  • retry يتجاوز سياسة إعادة المحاولة الافتراضية — البريد مهم، لذا نعيد المحاولة 5 مرات
  • إلقاء خطأ يُطلق إعادة المحاولة تلقائيًا
  • القيمة المُرجعة تُخزن وتظهر في لوحة التحكم لتسهيل التصحيح

الخطوة 4: إنشاء مهمة توليد الصورة الرمزية

أنشئ ملف 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 };
  },
});

الخطوة 5: إنشاء مهمة مزامنة CRM

أنشئ ملف 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 };
  },
});

لاحظ إعدادات إعادة المحاولة هنا — واجهات CRM البرمجية قد تكون غير مستقرة، لذا نستخدم تراجعًا أكثر قوة (factor: 3) مع حد أقصى للمهلة يبلغ 30 ثانية.

الخطوة 6: بناء سير عمل الإعداد متعدد الخطوات

الآن الجزء القوي — تنسيق جميع المهام في سير عمل واحد. أنشئ 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,
    });
 
    // الخطوة 1: إرسال بريد الترحيب وإنشاء الصورة الرمزية بالتوازي
    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,
    });
 
    // الخطوة 2: مزامنة مع 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 });
 
    // الخطوة 3: جدولة بريد متابعة بعد 3 أيام
    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 };
  },
});

يوضح سير العمل هذا عدة ميزات قوية في Trigger.dev:

  • triggerAndWait() — يُطلق مهمة فرعية وينتظر اكتمالها، ويُرجع النتيجة
  • Promise.all() — يشغل المهام المستقلة بالتوازي لأداء أفضل
  • trigger() مع delay — يجدول مهمة للتشغيل مستقبلاً (بعد 3 أيام)
  • تنسيق سير العمل — مهمة أب واحدة تنسق عدة مهام فرعية

الخطوة 7: إضافة مهمة Cron مجدولة

أنشئ src/trigger/daily-digest.ts لملخص يومي للمسؤول:

import { schedules, logger } from "@trigger.dev/sdk/v3";
 
export const dailySignupDigest = schedules.task({
  id: "daily-signup-digest",
  cron: "0 9 * * *", // كل يوم الساعة 9:00 صباحًا بتوقيت 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(),
    },
  ];
}

دالة schedules.task() تنشئ مهمة مُفعّلة بجدول cron. تعبير cron 0 9 * * * يعني "كل يوم الساعة 9:00 صباحًا بتوقيت UTC".

الخطوة 8: إطلاق المهام من Next.js

الآن اربط سير العمل بتطبيق Next.js. أنشئ مسار API في 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,
  });
}

ملاحظة مهمة: tasks.trigger() يعود فورًا — لا ينتظر اكتمال المهمة الخلفية. يحصل المستخدم على استجابة فورية بينما يعمل سير عمل الإعداد بشكل غير متزامن.

يمكنك أيضًا إطلاق المهام من Server Actions. أنشئ 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 };
}

الخطوة 9: بناء نموذج تسجيل بسيط

أنشئ 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(
        `Signed up successfully! Onboarding run: ${result.runId}`
      );
    } catch (error) {
      setStatus("Something went wrong. Please try again.");
    } 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">
          التسجيل
        </h1>
        <form action={handleSubmit} className="space-y-4">
          <div>
            <label
              htmlFor="name"
              className="block text-sm font-medium text-gray-700"
            >
              الاسم
            </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"
            >
              البريد الإلكتروني
            </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 ? "جارِ التسجيل..." : "سجّل الآن"}
          </button>
        </form>
        {status && (
          <p className="mt-4 rounded bg-green-50 p-3 text-sm text-green-700">
            {status}
          </p>
        )}
      </div>
    </main>
  );
}

الخطوة 10: تشغيل خادم التطوير

شغّل كلاً من خادم Next.js التطويري وعامل Trigger.dev:

# المحطة الأولى: Next.js
npm run dev
 
# المحطة الثانية: عامل Trigger.dev
npx trigger.dev@latest dev

أمر Trigger.dev dev يتصل بسحابة Trigger.dev ويشغل مهامك محليًا. افتح لوحة تحكم Trigger.dev لرؤية السجلات في الوقت الفعلي.

الآن اختبر التدفق:

  1. افتح تطبيقك على http://localhost:3000
  2. املأ نموذج التسجيل وأرسله
  3. شاهد لوحة تحكم Trigger.dev — سترى سير عمل الإعداد يبدأ، مهمة بريد الترحيب والصورة الرمزية تعملان بالتوازي، ثم مزامنة CRM، وأخيرًا جدولة بريد المتابعة بعد 3 أيام

الخطوة 11: معالجة الأخطاء واستراتيجيات إعادة المحاولة

يوفر Trigger.dev تحكمًا دقيقًا في معالجة الأخطاء. لنعزز مهمة بريد الترحيب:

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) => {
    // تنفيذ المهمة...
  },
});

يعمل callback الـ onFailure فقط بعد استنفاد جميع محاولات إعادة المحاولة — إنها فرصتك الأخيرة للتعامل مع الفشل.

يمكنك أيضًا استخدام retry.onThrow للتحكم الدقيق في إعادة المحاولة داخل المهمة:

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 });
};

الخطوة 12: إضافة البيانات الوصفية والعلامات

العلامات والبيانات الوصفية تجعل المهام قابلة للبحث والتصفية في لوحة التحكم:

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");
 
    // ... معالجة الطلب ...
 
    metadata.set("status", "completed");
 
    return { processed: true };
  },
});

الخطوة 13: العمليات الدفعية مع batchTrigger

عندما تحتاج لمعالجة عناصر متعددة، استخدم الإطلاق الدفعي:

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 العناصر الدفعية بتزامن محكوم، مما يمنعك من إثقال الخدمات الخارجية.

الخطوة 14: المتغيرات البيئية والإعدادات

أضف متغيراتك البيئية إلى .env.local:

# Trigger.dev
TRIGGER_SECRET_KEY=tr_dev_your_secret_key
 
# البريد الإلكتروني (Resend)
RESEND_API_KEY=re_your_api_key
 
# CRM
CRM_API_URL=https://api.your-crm.com
CRM_API_KEY=your_crm_key
 
# الإشعارات
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/webhook

للإنتاج، أضف نفس المتغيرات في لوحة تحكم Trigger.dev تحت إعدادات المشروع.

الخطوة 15: النشر في الإنتاج

انشر مهامك على سحابة Trigger.dev:

npx trigger.dev@latest deploy

يقوم هذا الأمر بـ:

  1. تجميع كود المهام
  2. رفعه إلى سحابة Trigger.dev
  3. جعل مهامك متاحة للإطلاق في الإنتاج

لدمج CI/CD، أضف أمر النشر إلى خط الأنابيب:

# .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 }}

الخطوة 16: المراقبة والتصحيح

توفر لوحة تحكم Trigger.dev مراقبة كاملة:

  • قائمة التشغيلات — رؤية جميع عمليات تنفيذ المهام مع الحالة والمدة
  • تفاصيل التشغيل — عرض السجلات والحمولة والمخرجات وسجل إعادة المحاولة
  • تتبع سير العمل — عرض مرئي لسير العمل متعدد الخطوات
  • الأخطاء — تصفية والبحث في التشغيلات الفاشلة مع آثار المكدس الكاملة
  • الجداول — إدارة ومراقبة مهام cron

اختبار التنفيذ

للتحقق من عمل كل شيء:

  1. شغّل خادم التطوير وعامل Trigger.dev في محطتين
  2. أرسل نموذج التسجيل — يجب أن ترى استجابة فورية
  3. تحقق من لوحة تحكم Trigger.dev — يجب أن يظهر سير عمل الإعداد
  4. اختبر معالجة الأخطاء — اكسر مفتاح API مؤقتًا وتحقق من عمل إعادة المحاولة
  5. تحقق من مهمة cron — يجب أن تظهر في تبويب الجداول

استكشاف الأخطاء وإصلاحها

المهام لا تظهر في لوحة التحكم: تأكد من تشغيل npx trigger.dev@latest dev. تحقق من أن trigger.config.ts يشير إلى المجلدات الصحيحة.

المهام تفشل بخطأ "Module not found": يجمع Trigger.dev المهام بشكل منفصل عن Next.js. تأكد من أن جميع الاستيرادات في ملفات المهام مستقلة ولا تستورد من وحدات Next.js المحددة.

إعادة المحاولة لا تعمل في التطوير: اضبط enabledInDev: true في إعدادات retries في trigger.config.ts.

هيكل المشروع

هذا هو هيكل المشروع النهائي:

trigger-onboarding/
  src/
    app/
      api/
        signup/
          route.ts          # مسار API لإطلاق الإعداد
      actions.ts            # Server Action لإرسال النموذج
      page.tsx              # واجهة نموذج التسجيل
    trigger/
      welcome-email.ts      # مهمة بريد الترحيب
      generate-avatar.ts    # مهمة إنشاء الصورة الرمزية
      sync-crm.ts           # مهمة مزامنة CRM
      onboarding-workflow.ts # منسق سير العمل متعدد الخطوات
      daily-digest.ts       # مهمة cron مجدولة
  trigger.config.ts         # إعدادات Trigger.dev
  .env.local                # المتغيرات البيئية

الخطوات التالية

الآن بعد أن أصبح لديك نظام مهام خلفية يعمل، فكر في:

  • إضافة المزيد من سير العمل — معالجة الطلبات، إنشاء التقارير، خطوط أنابيب البيانات
  • تنفيذ عدم التكرار — استخدم مفاتيح فريدة لمنع تنفيذ المهام المكررة
  • إعداد التنبيهات — اضبط إشعارات Slack أو البريد للمهام الفاشلة
  • استخدام Trigger.dev مع قاعدة بيانات — ادمج مع Prisma أو Drizzle
  • استكشاف أنماط التوزيع — استخدم batchTriggerAndWait لمعالجة مجموعات بيانات كبيرة
  • الاستضافة الذاتية — يمكن استضافة Trigger.dev v3 ذاتيًا إذا كنت بحاجة للتشغيل على بنيتك التحتية

الخلاصة

يجلب Trigger.dev v3 معالجة المهام الخلفية إلى نظام TypeScript البيئي مع تجربة مطور تبدو أصلية لـ Next.js. بدلاً من إدارة طوابير Redis وعمليات العمال ومنطق إعادة المحاولة بنفسك، تكتب دوال TypeScript عادية ويتعامل Trigger.dev مع البنية التحتية.

في هذا الدرس، بنيت نظام أتمتة إعداد مستخدمين كامل مع تنفيذ مهام متوازي وسير عمل متعدد الخطوات ومهام cron مجدولة ومعالجة أخطاء مع إعادة المحاولة ونشر في الإنتاج. تنطبق نفس الأنماط على أي احتياج معالجة خلفية — من إرسال البريد إلى معالجة المدفوعات إلى تشغيل خطوط أنابيب الذكاء الاصطناعي.

النقاط الرئيسية:

  1. المهام هي مجرد دوال TypeScript — لا حاجة لتعلم صيغة خاصة
  2. triggerAndWait يمكّن تنسيق سير العمل — ركّب سير عمل معقد من مهام بسيطة
  3. إعادة المحاولة ومعالجة الأخطاء مدمجة — اضبط مرة واحدة، وتعامل مع الفشل بأناقة
  4. المهام المجدولة تحل محل خوادم cron — لا حاجة لبنية تحتية لإدارة المهام المتكررة
  5. لوحة التحكم توفر مراقبة كاملة — صحح مشاكل الإنتاج بالسجلات والتتبعات والبيانات الوصفية

هل تريد قراءة المزيد من الدروس التعليمية؟ تحقق من أحدث درس تعليمي لدينا على بناء تطبيق ويب متكامل باستخدام Nuxt 4 و Vue 3.

ناقش مشروعك معنا

نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.

دعنا نجد أفضل الحلول لاحتياجاتك.

مقالات ذات صلة