وداعاً للاستعلام الدوري (Polling)! كيف غيرت الـ Webhooks طريقة بناء تطبيقاتي لتصبح لحظية وفعالة

يا جماعة الخير، الله يمسيكم بالخير. اسمي أبو عمر، قضيت سنين عمري بين الأكواد والخوارزميات في فلسطين وخارجها، وشفت العجب في عالم البرمجة. اليوم بدي أحكيلكم قصة صارت معي، قصة علمتني درس كبير في تصميم الأنظمة، درس عن الفرق بين إنك تضل “تِدُق الباب” كل شوي، وبين إنه الباب ينفتحلك لحاله لما الأكل يجهز.

قبل كم سنة، كنت شغال على نظام مربوط مع بوابة دفع إلكتروني. الفكرة كانت بسيطة: المستخدم بيدفع، ولما تتم العملية بنجاح، بنحدّث حالة الطلب في قاعدة البيانات تبعتنا وبنرسل إيميل تأكيد للمستخدم. في البداية، الحل اللي خطر ببالي كان “الاستعلام الدوري” أو الـ Polling. عملت سكربت بسيط يسأل واجهة برمجة التطبيقات (API) تبعت بوابة الدفع كل 5 ثواني: “يا جماعة، شو صار في الدفعة رقم 123؟ خلصت ولا لسا؟”.

في أول أسبوع، كان كل شي تمام. النظام شغال، والعميل مبسوط. لكن مع زيادة عدد المستخدمين والعمليات، بدأت الكارثة. الخادم (السيرفر) تبعنا صار زي واحد بركض ماراثون وهو حامل أثقال. استهلاك المعالج (CPU) في السما، استهلاك الشبكة عالي، وفواتير الاستضافة صارت تزيد. والأسوأ من هيك، إنه حتى مع كل هاد الضغط، التحديث ما كان فوري! كان في تأخير ممكن يوصل لـ 5 ثواني. العميل صار يشتكي: “دفعت والمصاري انخصمت، ولسه ما وصلني تأكيد!”. والمصيبة الأكبر صارت لما بوابة الدفع عملتلنا حظر مؤقت (rate limit) لأنه اعتبرت طلباتنا الكثيفة هجوم عليها. وقتها حسيت حالي محشور في زاوية، والنظام اللي بنيته صار عبء بدل ما يكون حل. زي ما بنحكي عنا: “إجا ليكحلها عماها”.

هون كانت نقطة التحول. بعد بحث وقراءة وتجربة، اكتشفت الحل السحري اللي أنقذني: الـ Webhooks. ومن يومها، تغيرت طريقة تفكيري في بناء أي نظام بيتعامل مع خدمات خارجية.


ما هو جحيم الاستعلام الدوري (Polling)؟

قبل ما نحكي عن المنقذ، خلينا نفهم المشكلة من جذورها. الـ Polling ببساطة هو إنك تخلي تطبيقك (العميل) يسأل الخادم بشكل متكرر: “هل في إشي جديد؟”.

تخيل حالك بتستنى مكالمة مهمة. في سيناريو الـ Polling، إنت رح تمسك التلفون كل دقيقة وتتصل على صاحبك وتسأله: “رن عليّ حدا؟”. رح تضل تكرر هالسؤال مرة ورا مرة، حتى لو 99% من المرات الجواب رح يكون “لأ”. هاد بالضبط اللي بيعمله الـ Polling.

كيف يعمل الـ Polling؟

الآلية بسيطة ومباشرة، وهذا سبب وقوع الكثير من المبرمجين في فخها في البداية:

  1. العميل (تطبيقك) يرسل طلب HTTP GET إلى الخادم (الـ API).
  2. الخادم يبحث عن أي تحديثات. إذا لم يجد شيئاً، يرجع استجابة فارغة أو حالة “لا يوجد جديد”.
  3. العميل ينتظر فترة زمنية محددة (مثلاً، ثانية واحدة).
  4. العميل يكرر الخطوة الأولى.

وهكذا دواليك في حلقة لا تنتهي من الطلبات والاستجابات التي لا فائدة من معظمها.

مساوئ الـ Polling التي عانيت منها شخصياً

  • إهدار الموارد (Resource Waste): كل طلب يعني استهلاك للمعالج، للذاكرة، وللباندويث على خادمك وعلى خادم الخدمة اللي بتستعلم منها. إنت بتدفع فلوس مقابل موارد بتروح على الفاضي.
  • التأخير الحتمي (Latency): التحديث ليس لحظياً أبداً. لو حددت فترة الاستعلام كل 5 ثواني، ففي أسوأ الأحوال، سيتأخر التحديث 5 ثواني كاملة. تقليل الفترة يزيد من إهدار الموارد، فإنت محشور بين خيارين أحلاهما مر.
  • صعوبة التوسع (Scalability Issues): النظام اللي بيشتغل كويس مع 10 مستخدمين، ممكن ينهار تماماً مع 1000 مستخدم. تخيل 1000 عميل بيسألوا الخادم كل ثانية! هذا كابوس لأي مدير نظام.
  • خطر الحظر (Rate Limiting): معظم الـ APIs المحترمة تضع حداً لعدد الطلبات اللي ممكن تعملها في الدقيقة أو الساعة. إذا تجاوزت هذا الحد، سيتم حظرك مؤقتاً أو حتى بشكل دائم. وهذا ما حدث معي بالضبط.

مثال كود بسيط لعملية Polling (باستخدام JavaScript)

هذا مثال بسيط جداً يوضح الفكرة. تخيل أننا نسأل عن حالة طلبية كل ثانيتين.


// Polling Example using JavaScript
const orderId = 'xyz-123';

// الدالة التي تسأل الخادم عن حالة الطلبية
async function checkOrderStatus() {
  console.log('Checking order status...');
  try {
    const response = await fetch(`/api/orders/${orderId}/status`);
    const data = await response.json();

    if (data.status === 'shipped') {
      console.log('Order has been shipped! Stopping polling.');
      clearInterval(pollingInterval); // نوقف الاستعلام عند الحصول على التحديث
      updateUI('Your order is on its way!');
    } else {
      console.log(`Current status: ${data.status}. Will check again...`);
    }
  } catch (error) {
    console.error('Failed to check order status:', error);
  }
}

// ابدأ الاستعلام الدوري كل 2000 ميللي ثانية (ثانيتين)
const pollingInterval = setInterval(checkOrderStatus, 2000);

الكود شغال، لكنه غير فعال بالمرة. والآن، لنتعرف على البطل الحقيقي للقصة.


الخلاص: كيف أنقذتني الـ Webhooks؟

الـ Webhook هو عكس الـ Polling تماماً. بدل ما إنت تضل تسأل “شو صار؟”، الخدمة الخارجية هي اللي بتخبرك لما يصير إشي. نرجع لمثال التلفون: بدل ما تتصل كل شوي، إنت بتعطي صاحبك رقمك وبتقله: “بس حدا يرن عليّ، إنت اتصل فيي مباشرة وخبرني”.

بالمصطلح التقني، الـ Webhook هو “رد نداء HTTP يتم تعريفه من قبل المستخدم” (User-defined HTTP callback). إنه ببساطة URL على الخادم تبعك، تقوم الخدمة الخارجية (مثل بوابة الدفع) بإرسال طلب HTTP POST إليه عندما يقع حدث معين (مثل إتمام عملية دفع).

كيف تعمل الـ Webhooks؟ (خطوات عملية)

العملية منظمة وفعالة جداً، “شغل مرتب” على الآخر:

  1. التسجيل (Registration): في لوحة تحكم الخدمة الخارجية (مثلاً Stripe أو GitHub)، تذهب إلى قسم الـ Webhooks وتضيف URL جديد. هذا الـ URL هو نقطة نهاية (Endpoint) في تطبيقك، مثل: https://my-awesome-app.com/webhooks/payment-handler. وتحدد أيضاً ما هي الأحداث التي تهمك (e.g., payment.succeeded).
  2. وقوع الحدث (Event Trigger): يقوم مستخدم بإجراء عملية دفع. داخل أنظمة بوابة الدفع، يقع حدث “نجاح الدفع”.
  3. الإشعار الفوري (Notification): فوراً، تقوم بوابة الدفع بإنشاء طلب HTTP POST وإرساله إلى الـ URL الذي سجلته. هذا الطلب يحتوي على “حمولة” (Payload)، وهي عبارة عن بيانات بصيغة JSON تحتوي على كل تفاصيل الحدث (رقم الطلب، المبلغ، العميل، إلخ).
  4. المعالجة والاستجابة (Processing & Acknowledgment):
    • يستقبل الخادم تبعك هذا الطلب.
    • (مهم جداً) يتحقق من أن الطلب قادم فعلاً من بوابة الدفع وليس من جهة خبيثة (سنتحدث عن هذا لاحقاً).
    • يقرأ البيانات من الـ Payload ويعالجها (يحدّث قاعدة البيانات، يرسل إيميل، …إلخ).
    • يرسل استجابة نجاح (مثل 200 OK) لبوابة الدفع ليخبرها بأنه استلم الإشعار بنجاح.

بناء أول Webhook Endpoint لك (مثال عملي باستخدام Node.js/Express)

خلونا نشوف كيف ممكن نبني نقطة نهاية بسيطة تستقبل Webhook. رح نستخدم Node.js وإطار العمل Express لأنه بسيط ومباشر.

1. إعداد الخادم

أولاً، تأكد من وجود Node.js وقم بتثبيت Express:


npm init -y
npm install express

2. إنشاء نقطة النهاية (Endpoint)

الآن، لنكتب كود الخادم. سنقوم بإنشاء ملف server.js.


const express = require('express');
const app = express();

// مهم: استخدم هذا الـ middleware لقراءة الـ JSON الخام
// نحتاجه للتحقق من التوقيع لاحقاً
app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf;
  }
}));

// هذا هو الـ Webhook Endpoint تبعنا
app.post('/webhooks/payment-handler', (req, res) => {
  console.log('Webhook received!');

  // الخطوة 1: التحقق من صحة الطلب (سنتحدث عنها بالتفصيل)
  // if (!isValidSignature(req)) {
  //   console.error('Invalid signature!');
  //   return res.status(400).send('Invalid signature.');
  // }
  
  // الخطوة 2: استخراج البيانات
  const event = req.body;

  // الخطوة 3: معالجة الحدث
  switch (event.type) {
    case 'payment.succeeded':
      const paymentIntent = event.data.object;
      console.log(`Payment for ${paymentIntent.amount} succeeded!`);
      // هنا تضع الكود الخاص بتحديث قاعدة بياناتك، إرسال إيميل، إلخ.
      // handlePaymentSucceeded(paymentIntent);
      break;
    case 'payment.failed':
      const paymentFailure = event.data.object;
      console.log(`Payment for ${paymentFailure.amount} failed.`);
      // هنا تضع الكود الخاص بإعلام المستخدم بالفشل.
      // handlePaymentFailed(paymentFailure);
      break;
    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  // الخطوة 4: إرسال استجابة نجاح لإعلام المرسل بأننا استلمنا الإشعار
  res.status(200).json({ received: true });
});

const PORT = 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

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


نصائح أبو عمر الذهبية للتعامل مع الـ Webhooks 👑

بناء Webhook سهل، لكن بناء نظام Webhook قوي وموثوق يتطلب الانتباه لبعض التفاصيل المهمة. هذه خلاصة خبرتي في هذا المجال:

نصيحة 1: لا تثق بأحد، تحقق من التوقيع دائماً!

الـ Webhook URL تبعك هو عنوان عام على الإنترنت. أي شخص يستطيع إرسال طلب POST إليه. كيف تضمن أن الطلب الذي وصلك هو من بوابة الدفع فعلاً وليس من مخترق يحاول العبث بنظامك؟

الحل هو التحقق من التوقيع (Signature Verification). معظم الخدمات المحترمة (Stripe, GitHub, Shopify) تقوم بتوقيع كل طلب Webhook ترسله. الآلية كالتالي:

  1. أنت تحصل على “مفتاح سري” (Webhook Secret) من لوحة تحكم الخدمة.
  2. الخدمة تقوم بإنشاء توقيع (Signature) عن طريق عمل Hash للـ Payload مع المفتاح السري، وترسل هذا التوقيع في هيدر الطلب (e.g., Stripe-Signature).
  3. في الكود تبعك، تقوم بنفس العملية: تعمل Hash للـ Payload الخام الذي استلمته مع نفس المفتاح السري.
  4. تقارن ناتج الـ Hash الذي حسبته مع التوقيع الموجود في الهيدر. إذا تطابقا، فالطلب أصلي. إذا لم يتطابقا، فتجاهل الطلب فوراً.

نصيحة عملية: لا تحاول بناء منطق التحقق من الصفر. استخدم المكتبات الرسمية التي توفرها الخدمة (e.g., stripe.webhooks.constructEvent) فهي تعالج كل الحالات الصعبة وتجعل العملية آمنة وسهلة.

نصيحة 2: اجعل معالج الـ Webhook سريعاً وخفيفاً

الخدمة التي أرسلت الـ Webhook تنتظر منك استجابة 200 OK بسرعة (عادة خلال ثوانٍ قليلة). إذا تأخرت في الرد، ستعتبر أن الإشعار لم يصلك، وستحاول إرساله مرة أخرى، مما قد يسبب مشاكل.

الحل: لا تقم بعمليات طويلة ومعقدة (مثل معالجة ملفات كبيرة، أو إرسال 1000 إيميل) مباشرة داخل دالة معالج الـ Webhook. اتبع هذه الاستراتيجية:

  1. استلم الطلب وتحقق من توقيعه.
  2. خذ البيانات المهمة من الـ Payload.
  3. أضف هذه البيانات كـ “مهمة” (Job) في طابور انتظار (Message Queue) مثل RabbitMQ, Redis (باستخدام BullMQ), أو AWS SQS.
  4. أرسل استجابة 200 OK فوراً.
  5. دع “عامل” (Worker) منفصل يسحب المهام من الطابور ويعالجها في الخلفية وبالسرعة التي تناسبه.

هذا التصميم يفصل بين استقبال الإشعار ومعالجته، مما يجعل نظامك قوياً وقادراً على التعامل مع آلاف الـ Webhooks في الدقيقة دون أي مشاكل.

نصيحة 3: كن مستعداً للطلبات المكررة (Idempotency)

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

الحل هو تصميم معالج “عَديم التأثر” (Idempotent). كل حدث Webhook يأتي عادة مع ID فريد (e.g., event.id). قبل معالجة أي حدث، تحقق في قاعدة بياناتك إذا كنت قد عالجت هذا الـ ID من قبل.

  • إذا كنت قد عالجته، تجاهل الطلب الحالي وأرسل 200 OK.
  • إذا لم تكن قد عالجته، قم بمعالجته ثم سجل الـ ID في قاعدة البيانات كـ “معالج”.

نصيحة 4: استخدم أدوات مثل `ngrok` للتطوير المحلي

أثناء التطوير، الخادم تبعك يعمل على جهازك المحلي (localhost)، وهو غير متاح على الإنترنت العام. إذن كيف ستستقبل الـ Webhooks من الخدمات الخارجية؟

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


ngrok http 3000

وستعطيك عنواناً عاماً مثل https://random-string.ngrok.io. يمكنك الآن استخدام هذا العنوان في لوحة تحكم الـ Webhooks، وأي طلب يتم إرساله إليه سيصل مباشرة إلى خادمك المحلي على البورت 3000. إنها أداة لا غنى عنها لتطوير واختبار الـ Webhooks.


الخلاصة: متى تختار هذا أو ذاك؟ ⚖️

بعد كل هذا الكلام، هل يعني أن الـ Polling سيء دائماً ويجب أن نرميه في سلة المهملات؟ ليس تماماً. لكل أداة مكانها وزمانها.

استخدم الاستعلام الدوري (Polling) عندما:

  • الـ API التي تتعامل معها قديمة ولا تدعم الـ Webhooks (وهذا نادر اليوم).
  • لا تحتاج إلى تحديثات لحظية على الإطلاق (مثلاً، تحديث تقرير يومي أو كل ساعة).
  • تطبيقك بسيط جداً ولا تريد تعقيد إعداد نقطة نهاية عامة والتعامل مع أمانها.

استخدم الخطافات الشبكية (Webhooks) عندما:

  • تحتاج إلى تفاعل لحظي أو شبه لحظي (Real-time). هذا هو الاستخدام الأساسي والأقوى.
  • تريد بناء نظام فعال، موفر للموارد، وقابل للتوسع بشكل كبير.
  • تتعامل مع أي خدمة حديثة تدعمها (وهي الغالبية العظمى اليوم: بوابات الدفع، خدمات Git، منصات التواصل، أدوات التشغيل الآلي، إلخ).

في النهاية، الانتقال من Polling إلى Webhooks كان نقلة نوعية في جودة وكفاءة الأنظمة التي أبنيها. لقد حررني من صداع مراقبة أداء الخوادم، وخفض التكاليف، وجعل تجربة المستخدم النهائي أفضل بكثير. نصيحتي لك يا صديقي المبرمج، إذا وجدت نفسك تكتب setInterval لتسأل API عن شيء ما، توقف لحظة وفكر: “هل يوجد Webhook لهذا؟”. في أغلب الأحيان، الإجابة ستكون نعم، وهذه “النعم” ستوفر عليك الكثير من الجهد والمشاكل في المستقبل.

يلا، شدوا حيلكم، وخلي شغلكم دايماً مرتب وفعال. 🚀

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

التوظيف وبناء الهوية التقنية

سيرتي الذاتية عبرت فلتر الـ ATS لكنها فشلت أمام المدير التقني: كيف أعدت بناءها لتتحدث لغة المهندسين؟

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

28 فبراير، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

خدمة واحدة فاشلة كادت أن تسقط النظام بأكمله: كيف أنقذني نمط ‘قاطع الدائرة’ (Circuit Breaker) من كارثة متتالية؟

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

27 فبراير، 2026 قراءة المزيد
اختبارات الاداء والجودة

لقد ‘هاجمت’ تطبيقي بنفسي عمداً: كيف كشفت لي ‘هندسة الفوضى’ نقاط الضعف التي لم تظهرها الاختبارات التقليدية

أشارككم قصة حقيقية حول إطلاق فاشل كاد أن يدمر سمعتنا، وكيف قادتنا هذه التجربة المريرة إلى تبني "هندسة الفوضى" (Chaos Engineering). اكتشفوا معنا كيف يمكن...

26 فبراير، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

عاصفة من الطلبات كادت أن تغرق تطبيقي: كيف أنقذتني طوابير الرسائل (Message Queues) من كارثة الجمعة السوداء؟

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

24 فبراير، 2026 قراءة المزيد
البودكاست