📘 دليل تعليمي عملي • NestJS Backend • SaaS Meta Ads

الدليل الشامل والكامل لمشروع SaaS Meta Ads

وثيقة تعليمية مرتبة بصيغة موقع HTML، موجهة لفهم بنية مشروع Back-end حقيقي مبني باستخدام NestJS، مع شرح تدريجي للمفاهيم، تدفق البيانات، الموديولات الأساسية، والأمثلة البرمجية بشكل واضح وسهل القراءة.

نوع المشروعمنصة SaaS لتحليل وإدارة إعلانات Meta
الإطار المستخدمNestJS + TypeScript + Node.js
الهدف التعليميفهم معماريّة Backend حقيقية خطوة بخطوة
أسلوب العرضRTL، منظم، وأقرب لكتاب تقني تعليمي

1) ما هو هذا المشروع أصلاً؟

تخيّل أن لديك عدة حملات إعلانية تعمل على Meta، وربما لاحقاً على TikTok أو Google. بدلاً من الدخول إلى كل منصة بشكل منفصل لمتابعة الأداء، يقوم هذا المشروع ببناء مركز قيادة موحد يجلب الأرقام، يحللها، يكتشف الخلل، ويرسل تنبيهات أو ينفذ إجراءات تلقائياً عند الحاجة.

الفكرة الجوهرية هنا ليست فقط عرض الأرقام، بل بناء نظام يقرأ الأداء ويحوّله إلى قرارات عملية.

ما الذي يفعله النظام؟

  • يربط الحساب الإعلاني بالمستخدم.
  • يجلب الإحصاءات من Meta Graph API.
  • يوحد البيانات ويحللها.
  • يستخدم AI لإعطاء توصيات مفهومة.
  • ينفذ قواعد أتمتة مثل إيقاف الحملات الخاسرة.
  • يرسل تنبيهات إلى الهاتف أو تيليجرام أو Slack.

لماذا هذا النوع من المشاريع مهم؟

  • يمثل مشروع SaaS حقيقي يمكن بيعه باشتراك شهري.
  • يجمع بين Backend Architecture و Integrations و AI.
  • يعلمك كيف تُبنى الأنظمة الكبيرة بشكل منظم.
  • يفتح الباب لإضافة TikTok أو Google Ads لاحقاً بسهولة.

2) الأساس الذي بنينا عليه المشروع (Tech Stack)

اعتمد المشروع على مجموعة تقنيات تكمل بعضها البعض. الفهم هنا مهم، لأن كل تقنية لها دور محدد داخل النظام.

التقنية الدور داخل المشروع لماذا اختيرت؟
Node.js محرك تشغيل JavaScript على السيرفر سريع ومناسب لـ I/O وطلبات APIs
TypeScript إضافة أنواع واضحة للكود تقليل الأخطاء وتحسين القراءة والتنظيم
NestJS Framework منظم لبناء Back-end معمارية قوية تشبه الأنظمة الكبيرة
TypeORM / PostgreSQL التعامل مع البيانات والجداول هيكلة واضحة وقاعدة بيانات قوية
Axios / HttpService التواصل مع خدمات خارجية إرسال طلبات HTTP إلى Meta وTelegram وغيرها

المفاهيم الأساسية في NestJS

Module Controller Service Entity Guard Filter
  • Module: صندوق يجمع الوظائف المتقاربة في مكان واحد.
  • Controller: يستقبل الطلب القادم من الواجهة أو التطبيق.
  • Service: ينفذ المنطق الفعلي في الخلفية.
  • Entity: يصف شكل الجدول داخل قاعدة البيانات.
  • Guard: يفحص هل للمستخدم الحق في الوصول أم لا.
  • Filter: يتعامل مع الأخطاء ويحوّلها إلى رد نظيف ومنظم.

3) جولة في النواة (The Root)

src/main.ts — الشرارة الأولى

هذا هو أول ملف يُنفذ عند تشغيل المشروع. دوره هو إنشاء التطبيق، تجهيز الإعدادات العامة، وإخبار السيرفر أن يبدأ الاستماع للطلبات.

main.ts
async function bootstrap() {
  // إنشاء التطبيق بناءً على الموديول الرئيسي
  const app = await NestFactory.create(AppModule);

  // كل الروابط ستبدأ بـ /api
  app.setGlobalPrefix('api');

  // إعداد Swagger لتوثيق الـ API
  const config = new DocumentBuilder()
    .setTitle('SaaS Meta')
    .build();

  // تشغيل السيرفر على المنفذ 3001
  await app.listen(3001);
}
عملياً، إذا لم يعمل main.ts فلن يعمل المشروع كله، لأنه نقطة الإقلاع الأساسية.

src/app.module.ts — الدماغ المركزي

هذا الملف يربط كل موديولات المشروع ببعضها. أي جزء غير مسجل هنا غالباً لن يكون معروفاً للتطبيق.

app.module.ts
@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TypeOrmModule.forRoot({ /* database config */ }),
    MetaAdsModule,
    AutomationModule,
    AuthModule,
    UsersModule,
    PaymentsModule,
  ],
})
export class AppModule {}

configuration.ts — الخزنة

هذا الملف يقرأ القيم السرية من .env، مثل مفاتيح Meta أو بيانات قاعدة البيانات. الهدف هو فصل الأسرار عن الكود.

.env مثال
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=secret
META_TOKEN=your_meta_token
GEMINI_API_KEY=your_gemini_key

4) موديول Meta Ads

هنا يبدأ المشروع بالتعامل المباشر مع Meta. هذه الطبقة مسؤولة عن الاتصال بـ Graph API، جلب البيانات، وتنظيفها لتصبح قابلة للفهم داخل النظام.

MetaAdsService — المفاوض

هذا الجزء يرسل الطلب إلى Meta ويسترجع الإحصاءات الأساسية للحملات.

fetchInsights()
async fetchInsights(adAccountId: string) {
  const url = `https://graph.facebook.com/v19.0/${adAccountId}/insights`;

  const response = await this.httpService.get(url, {
    params: {
      access_token: this.token,
      fields: 'campaign_name,impressions,clicks,spend,ctr'
    }
  });

  return response.data;
}
  • يبني رابط API الخاص بالحساب الإعلاني.
  • يرسل access_token حتى تسمح Meta بإرجاع البيانات.
  • يحدد الحقول المطلوبة فقط لتقليل الحمل غير الضروري.

MetaNormalizer — عامل النظافة

بيانات Meta غالباً تعود بصيغة خام وغير موحدة. لذلك نحتاج طبقة تنظفها وتحولها إلى شكل موحد يعتمد عليه باقي المشروع.

normalize()
static normalize(raw: any) {
  return {
    campaignId: raw.campaign_id,
    campaignName: raw.campaign_name || 'اسم غير معروف',
    impressions: parseInt(raw.impressions || '0'),
    spend: parseFloat(raw.spend || '0'),
    source: 'meta'
  };
}
هذه الطبقة مهمة جداً. بدونها سيبقى كل مصدر بيانات يعيد لنا بنية مختلفة، وهذا يجعل النظام هشاً وصعب التوسعة لاحقاً.

5) موديول التحليلات والذكاء الاصطناعي

بعد جلب البيانات، لا يكفي عرض الأرقام كما هي. هنا يبدأ دور التحليل: هل الحملة جيدة أم سيئة؟ هل الصورة ضعيفة؟ هل تكلفة النقرة مرتفعة؟ هل هناك شيء يستدعي تنبيهاً فورياً؟

AnalyticsService — المحلل

analyzeCampaign()
analyzeCampaign(data: any) {
  const insights = [];

  if (data.ctr < 1.0) {
    insights.push({
      type: 'warning',
      code: 'LOW_CTR',
      message: 'نسبة النقر ضعيفة! الجمهور يرى الإعلان ولكن لا ينقر عليه.',
      recommendation: 'جرب تغيير صورة الإعلان أو العنوان لجذب الانتباه أكثر.'
    });
  }

  if (data.cpc > 2.0) {
    insights.push({
      type: 'danger',
      message: 'تكلفة النقرة غالية جداً!',
      recommendation: 'راجع استهداف الجمهور، ربما تستهدف جمهوراً منافساً بشدة.'
    });
  }

  return insights;
}

هذه التحليلات هي منطق برمجي صريح. أي أنها لا تعتمد على AI فقط، بل على قواعد واضحة يمكن الوثوق بها وتعديلها بسهولة.

AiService — الخبير الذكي

هنا يدخل الذكاء الاصطناعي ليحوّل الأرقام إلى شرح بشري قابل للفهم. يمكنه مثلاً أن يقرأ الأداء ويكتب توصية بلغة واضحة، أو حتى يحلل صورة الإعلان إذا كان لدينا Vision model.

استخدامات AI داخل المشروع

  • شرح أداء الحملة بلغة مفهومة.
  • اقتراح تحسينات على الجمهور أو الكرياتيف.
  • تحليل صورة الإعلان من ناحية الجاذبية والوضوح.
  • اكتشاف الأنماط التي قد لا يلتقطها الشرط البرمجي البسيط.

كيف تتكامل الخدمة مع النظام؟

  • تستقبل البيانات المنظفة من النظام.
  • يتم بناء Prompt واضح ومنضبط.
  • يعيد النموذج توصية أو ملخصاً أو تفسيراً.
  • يُعرض الناتج للمستخدم أو يستخدم ضمن التنبيهات.

6) محرك الأتمتة (Automation Engine)

هذا الجزء هو الذي يجعل المشروع أكثر من مجرد لوحة تحكم. هنا ننتقل من “المراقبة” إلى “التنفيذ الذكي”. المستخدم يضع قاعدة، والنظام ينفذها أوتوماتيكياً دون أن ينتظر تدخله اليدوي.

RuleEngineService — المراقب

evaluateRule()
async evaluateRule(rule: Rule) {
  // 1) جلب البيانات الحالية من Meta
  const currentData = await this.metaService.fetchInsights(rule.targetId);

  // 2) فحص الشرط
  const shouldTrigger = this.checkConditions(rule, currentData);

  if (shouldTrigger) {
    // 3) تنفيذ الإجراء المطلوب
    if (rule.action === 'PAUSE') {
      await this.metaService.updateStatus(rule.targetId, 'PAUSED');
      this.logger.log(`تم إيقاف الحملة ${rule.targetId} بطلب من الأتمتة.`);
    }
  }
}

الفكرة العملية للقواعد

  • إذا تجاوز الصرف 100 دولار بدون مبيعات → أوقف الحملة.
  • إذا انخفض CTR عن حد معين → أرسل تحذيراً.
  • إذا ارتفع CPA كثيراً → أبلغ المستخدم فوراً.

Cron Jobs — التوقيت

لكي تعمل الأتمتة تلقائياً، لا بد من وجود آلية زمنية تكرر الفحص كل فترة. هنا يأتي دور المهام المجدولة.

مثال Cron
@Cron('0 * * * *')
handleHourlyChecks() {
  // كل ساعة يتم فحص القواعد المسجلة
}

7) التنبيهات

عندما يكتشف النظام مشكلة أو ينفذ إجراءً مهماً، يجب أن يبلغ المستخدم فوراً. التنبيه ليس مجرد ميزة إضافية؛ بل جزء أساسي من قيمة المنتج.

NotificationService — ساعي البريد

send()
async send(message: string, channels: string[]) {
  for (const channel of channels) {
    if (channel === 'telegram') {
      await this.http.post('https://api.telegram.org/...', { text: message });
    }

    if (channel === 'fcm') {
      await this.firebase.send({ body: message });
    }
  }
}
القناة الاستخدام الفائدة
Telegram رسائل مباشرة عبر Bot سريع وسهل للمطور والمستخدم
Slack إشعار لفريق العمل مفيد للشركات والفرق
FCM تنبيه على الهاتف أفضل تجربة فورية للمستخدم

8) الدفع والفلوس

بما أن هذا مشروع SaaS، فهو يحتاج نظام اشتراكات واضح. المستخدم يدفع، والنظام يفعّل صلاحياته تلقائياً. هذا الجزء يربط المنتج بنموذج الربح.

Webhook مثال
@Post('callback')
async handlePayment(@Body() data: any) {
  const isValid = this.verifySignature(data);

  if (isValid && data.success) {
    await this.users.activateSubscription(data.userId);
  }
}
  • مزود الدفع يرسل Webhook عند نجاح العملية.
  • السيرفر يتحقق من التوقيع الرقمي.
  • إذا كانت العملية صحيحة، يفعّل الاشتراك مباشرة.
التحقق من التوقيع ليس تفصيلاً ثانوياً. إذا أُهمل، يمكن لأي جهة أن ترسل طلباً مزوراً وتفعّل اشتراكات غير حقيقية.

9) الأمان والاشتراكات

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

SubscriptionGuard — الحارس

canActivate()
async canActivate(context: ExecutionContext) {
  const request = context.switchToHttp().getRequest();
  const userId = request.headers['x-user-id'];

  const user = await this.userRepo.findOne(userId);

  if (user.tier === 'pro') return true;

  if (user.requestCount < 10) {
    user.requestCount++;
    await this.userRepo.save(user);
    return true;
  }

  throw new HttpException('انتهت النسخة التجريبية!', 402);
}
  • يفحص هوية المستخدم.
  • يجلب خطته الحالية من قاعدة البيانات.
  • يسمح أو يمنع الوصول حسب السياسة المقررة.

10) التعامل مع الأخطاء

الأخطاء ستحدث دائماً: انقطاع من Meta، توكن منتهي، مشكلة قاعدة بيانات، أو خطأ برمجي. النظام الجيد لا ينهار بصمت، بل يعيد رسالة مفهومة ومنظمة.

HttpExceptionFilter
catch(exception: unknown, host: ArgumentsHost) {
  const status = exception.getStatus();
  const message = exception.getResponse();

  this.response.status(status).json({
    code: status,
    timestamp: new Date().toISOString(),
    error: 'حدثت مشكلة في الطلب',
    detail: message
  });
}
وجود Error Filter موحّد يجعل الـ API احترافيًا أكثر، ويسهل على الواجهة الأمامية التعامل مع الأخطاء بطريقة متسقة.

11) الجداول والبيانات

لا يوجد مشروع Back-end حقيقي من دون طبقة بيانات واضحة. الكيانات أو الـ Entities هي الصياغة التي نستخدمها لوصف الجداول داخل قاعدة البيانات.

User Entity
@Entity('users')
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ unique: true })
  email: string;

  @Column({ default: 'free' })
  subscriptionTier: string;

  @Column({ default: 0 })
  requestCount: number;

  @CreateDateColumn()
  createdAt: Date;
}

لماذا هذا مهم؟

  • يوضح ما الذي نخزنه لكل مستخدم.
  • يسهل إنشاء الجداول تلقائياً عبر ORM.
  • يجعل العلاقة بين الكود والبيانات واضحة وقابلة للصيانة.

12) قاموس المصطلحات

المصطلح المعنى المبسط
API النافذة التي يتواصل عبرها النظام مع العالم الخارجي
JSON صيغة نصية منظمة لتبادل البيانات
Endpoint رابط أو مسار لميزة محددة داخل الـ API
Database الخزنة التي نحفظ فيها المستخدمين والاشتراكات والقواعد
Environment Variables إعدادات وأسرار تحفظ خارج الكود داخل ملف .env
Dependency Injection طريقة NestJS الذكية لتمرير الخدمات بدون تعقيد يدوي

13) ملخص رحلة البيانات (Data Journey)

هذا القسم يلخص ما يحدث تقنياً منذ لحظة ضغط المستخدم على الزر وحتى ظهور النتيجة أمامه.

1. المستخدم يضغط على زر تحليل حملة Meta من الواجهة.
2. Controller يستقبل الطلب ويحدد الخدمة المناسبة.
3. SubscriptionGuard يفحص هل يملك المستخدم صلاحية الوصول.
4. MetaAdsService يتحدث مع Meta ويجلب البيانات الخام.
5. MetaNormalizer ينظف البيانات ويحولها لصيغة موحدة.
6. AnalyticsService يفحص المؤشرات ويكتشف المشاكل.
7. AiService يضيف طبقة تفسير ذكية وتوصيات بشرية.
8. RuleEngine يفحص إن كانت هناك قواعد يجب تنفيذها.
9. NotificationService يرسل تنبيهاً إذا لزم الأمر.
10. يعيد السيرفر رداً منظماً بصيغة JSON للواجهة.

14) تفاصيل إضافية مهمة لفهم المشروع بعمق

أ) ما الذي يجعل هذا المشروع احترافيًا؟

  • الفصل الواضح بين الطبقات: Controller / Service / Entity / Guard.
  • إمكانية التوسعة: إضافة TikTok أو Google Ads لاحقاً دون تخريب المعمارية.
  • وجود أتمتة وتنبيهات، وليس مجرد عرض للبيانات.
  • إدارة اشتراكات فعلية، ما يجعله SaaS قابلاً للبيع.

ب) ما الذي يمكنك تحسينه لاحقاً؟

تحسينات هندسية

  • إضافة Redis للـ Cache وتقليل استدعاءات Meta.
  • استخدام Queue jobs للمهام الثقيلة.
  • إضافة Logger احترافي مثل Winston أو Pino.
  • كتابة Unit Tests وIntegration Tests.

تحسينات منتجية

  • خطط اشتراك متعددة مع Limits واضحة.
  • واجهة Dashboard متقدمة ورسوم بيانية أكثر عمقاً.
  • تقارير PDF أو أسبوعية للمستخدمين.
  • دعم أكثر من شبكة إعلانية.

ج) لماذا هذا المشروع ممتاز للتعلّم؟

لأنه لا يعلمك فقط كيف تكتب Controller أو Service، بل يعلمك كيف تفكر كنظام: كيف تُدخل البيانات، كيف تنظفها، كيف تحللها، كيف تحميها، كيف تربطها بالدفع، وكيف تجعلها تنتج قيمة تجارية فعلية.

15) الخلاصة العملية

هذا المشروع مثال ممتاز على Backend حقيقي مبني بعقلية منتج SaaS، وليس مجرد تدريب أكاديمي. إذا فهمت هذا المستند جيداً، فأنت لم تتعلم NestJS فقط، بل بدأت تفهم فعلياً كيف تُبنى الأنظمة التي تجمع البيانات، تحللها، تتخذ القرار، وتحول كل ذلك إلى خدمة قابلة للبيع والاشتراك.

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

تم تنسيق هذه النسخة كصفحة HTML تعليمية أنيقة، باتجاه من اليمين إلى اليسار، وبأقسام واضحة وأمثلة كود داخل صناديق منظمة لسهولة القراءة والمراجعة.