كنت أرسل 10 طلبات API لصفحة واحدة: كيف أنقذتني GraphQL من شلالات الشبكة؟

قصة فنجان قهوة وتطبيق بطيء

يا جماعة الخير، السلام عليكم. معكم أخوكم أبو عمر.

قبل كم سنة، كنت شغال على مشروع داشبورد (لوحة تحكم) لعميل مهم. كانت الفكرة بسيطة: صفحة رئيسية تعرض للمستخدم معلوماته الشخصية، آخر مقالات كتبها، قائمة بأصدقائه، وآخر التعليقات على مقالاته. “زي الليرة”، قلت لحالي، الشغل مرتب والـ API endpoints اللي بنيتها باستخدام REST كانت نظيفة ومقسمة بشكل منطقي.

  • GET /api/user/{id} لجلب معلومات المستخدم.
  • GET /api/user/{id}/posts لجلب مقالاته.
  • GET /api/user/{id}/friends لجلب أصدقائه.
  • GET /api/posts/{postId}/comments لجلب التعليقات على كل مقال… وهكذا.

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

فتحت الـ Developer Tools في المتصفح على تاب الـ Network، وحطيت محاكاة لاتصال 3G بطيء… وهون كانت الصدمة. شفت الطلبات بتنزل ورا بعضها زي شلال المي، واحد بستنى الثاني ليخلص. 💧

طلبت معلومات المستخدم، وبعد ما وصلت، طلبت مقالاته، وبعد ما وصلت، بلشت أطلب التعليقات لكل مقال… والمجموع كان حوالي 10 طلبات منفصلة عشان أعرض صفحة واحدة! أدركت وقتها إني وقعت في فخ كلاسيكي اسمه “شلالات الشبكة” أو Network Waterfalls.

ما هي “شلالات الشبكة” (Network Waterfalls) وليش هي كارثة؟

ببساطة، شلال الشبكة هو سلسلة من طلبات الشبكة (API calls) اللي بتعتمد على بعضها. يعني، ما بتقدر تبدأ الطلب “ب” إلا لما يرجعلك جواب الطلب “أ”.

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

تشريح المشكلة مع REST API

معمارية REST التقليدية، رغم قوتها وجمالها في تنظيم الـ backend، بتشجع على هاد النمط بدون قصد. ليش؟ لأنها مبنية على فكرة “المصادر” (Resources). كل مصدر إله endpoint خاص فيه.

في قصتي، كان عندي المصادر التالية: User, Post, Comment, Friend.

عشان أعرض الصفحة، كنت بحاجة لـ:

  1. أجيب بيانات الـ User.
  2. من بيانات الـ User، آخذ الـ ID وأجيب الـ Posts تبعونه.
  3. من بيانات الـ User، آخذ الـ ID وأجيب الـ Friends تبعونه.
  4. لكل Post جبته، آخذ الـ ID وأجيب الـ Comments الخاصة فيه.

هذا الأسلوب بيخلق مشكلتين رئيسيتين:

  • Under-fetching (الجلب الناقص): أول طلب بجيبلك بس معلومات المستخدم، بس أنت محتاج كمان مقالاته وأصدقائه، فبتضطر تعمل طلبات إضافية.
  • Over-fetching (الجلب الزائد): أحيانًا، endpoint معين برجعلك بيانات أكثر من اللي بتحتاجها. يمكن GET /api/user/{id} برجع 50 حقل عن المستخدم، وأنت كل اللي بدك إياه هو اسمه وصورته الشخصية. هاي بيانات إضافية بتسافر عبر الشبكة بدون أي داعي.

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

الحل السحري: كيف دخلت GraphQL على الخط 🚀

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

شو هي GraphQL أصلاً؟

GraphQL هي لغة استعلام (Query Language) للـ API تبعك، وكمان بيئة تشغيل في السيرفر لتنفيذ هاي الاستعلامات. الفكرة العبقرية فيها هي إنها بتغير موازين القوى.

بدل ما السيرفر يقرر شكل البيانات اللي برجعها، الـ Client (العميل/المتصفح) هو اللي بطلب شكل البيانات اللي بده إياها بالزبط، لا زيادة ولا نقصان، وكل هاد بطلب واحد فقط!

خليني أرجع لمثال المطعم:

  • REST API: زي لما تطلب وجبة “Set Menu”. يمكن تجيك الشوربة والطبق الرئيسي والسلطة والحلو، حتى لو أنت بس بدك الطبق الرئيسي. (Over-fetching). أو لازم تطلب كل صحن لحال. (Under-fetching/Waterfall).
  • GraphQL: زي لما تطلب من الشيف طلب مخصص: “بدي قطعة ستيك medium-well، مع شوية خضار سوتيه، وبطاطا مهروسة بدون زبدة”. بتوصف طلبك بالكامل، وبوصلك على صحن واحد زي ما بدك بالزبط.

تطبيق الحل على مشكلتنا (أمثلة كود)

خلينا نشوف الفرق بشكل عملي. في السابق، الكود في الـ frontend كان ممكن يشبه هيك (باستخدام JavaScript كمثال):


// قبل GraphQL: شلال من طلبات REST
async function getDashboardData_REST(userId) {
  try {
    // الطلب الأول
    const userRes = await fetch(`/api/user/${userId}`);
    const user = await userRes.json();

    // الطلب الثاني (يعتمد على الأول)
    const postsRes = await fetch(`/api/user/${userId}/posts`);
    const posts = await postsRes.json();

    // الطلب الثالث (يعتمد على الأول)
    const friendsRes = await fetch(`/api/user/${userId}/friends`);
    const friends = await friendsRes.json();

    // ... وتخيل كمان 7 طلبات للتعليقات وغيرها!

    // بالنهاية، ندمج كل البيانات لعرض الصفحة
    console.log("Done fetching, finally!");
    return { user, posts, friends };
  } catch (error) {
    console.error("Failed to fetch data:", error);
  }
}

هسا، شوفوا كيف صار الوضع مع GraphQL. أولاً، بنكتب “استعلام” واحد يوصف كل البيانات اللي بنحتاجها:


# استعلام GraphQL واحد يطلب كل شيء
query GetDashboardData($userId: ID!) {
  user(id: $userId) {
    name
    profilePicture
    posts(first: 5) {
      title
      createdAt
      comments(first: 2) {
        body
        author {
          name
        }
      }
    }
    friends(first: 8) {
      name
      profilePicture
    }
  }
}

لاحظوا كيف الاستعلام بيشبه بنية JSON. أنت بتطلب الحقول اللي بدك إياها بالزبط. بدك 5 مقالات بس؟ بتحدد (first: 5). بدك بس اسم وصورة الصديق؟ بتطلب بس name و profilePicture.

والكود في الـ frontend بصير أبسط بكثير:


// بعد GraphQL: طلب واحد فقط!
async function getDashboardData_GraphQL(userId) {
  const query = `
    query GetDashboardData($userId: ID!) {
      user(id: $userId) {
        name
        profilePicture
        posts(first: 5) {
          # ... باقي الاستعلام
        }
        friends(first: 8) {
          # ... باقي الاستعلام
        }
      }
    }
  `;

  try {
    const res = await fetch('/graphql', { // كل الطلبات بتروح لنفس الـ endpoint
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query,
        variables: { userId }, // بنمرر الـ ID كمتغير
      }),
    });

    const { data } = await res.json();
    console.log("Done fetching, that was fast!");
    return data; // 'data' يحتوي على كل شيء جاهز للعرض
  } catch (error) {
    console.error("Failed to fetch data:", error);
  }
}

النتيجة؟ طلب شبكة واحد، استجابة واحدة تحتوي على كل البيانات اللي طلبتها بالضبط. شلال الشبكة تبخر. والأداء تحسن بشكل خرافي.

نصائح من خبرة أبو عمر 💡

بعد ما اشتغلت على مشاريع كثيرة باستخدام GraphQL، تعلمت كم شغلة بحب أشاركم إياها.

مش كل إشي بده GraphQL

صحيح أنا بحب GraphQL، بس هي مش الحل لكل المشاكل. “ما في داعي تجيب بازوكا عشان تقتل دبّانة”. إذا كان عندك تطبيق بسيط جدًا، أو مايكروسيرفس عنده وظيفة محددة جدًا (مثلاً، خدمة إرسال إيميلات)، يمكن REST API يكون أسرع وأسهل في التنفيذ. GraphQL بتلمع وبتظهر قوتها الحقيقية في التطبيقات المعقدة اللي فيها بيانات متشابكة (زي الشبكات الاجتماعية، لوحات التحكم، تطبيقات التجارة الإلكترونية الكبيرة) أو لما يكون عندك عدة عملاء (تطبيق ويب، تطبيق موبايل iOS، تطبيق أندرويد) وكل واحد فيهم بيحتاج بيانات مختلفة.

فكر في “العميل” أولاً (Client-First)

أجمل شيء في GraphQL إنها بتجبرك كمطور backend تفكر من وجهة نظر مطور الـ frontend أو الموبايل. بدل ما تفكر “شو المصادر اللي عندي؟”، بتصير تفكر “شو الشاشات اللي عندي وكل شاشة شو البيانات اللي بتحتاجها؟”. هذا التحول في طريقة التفكير بحد ذاته بيؤدي لتصميم API أفضل وأكثر كفاءة.

انتبه لمشكلة N+1 في الـ Backend

هاي نصيحة ذهبية ومهمة جدًا. GraphQL بتحل مشكلة شلال الشبكة من جهة العميل (Client)، لكنها ممكن تسبب مشكلة مشابهة في جهة الخادم (Server) مع قاعدة البيانات إذا ما انتبهت. هاي المشكلة اسمها “N+1 Query Problem”.

تخيل الاستعلام السابق: أنت طلبت مستخدم واحد ومقالاته الخمسة. ممكن السيرفر ينفذ:

  • استعلام واحد لجلب المستخدم (SELECT * FROM users WHERE id = ?)
  • ثم 5 استعلامات، واحد لكل مقال، لجلب التعليقات تبعته! (SELECT * FROM comments WHERE post_id = ?)

هيك بصير عندك 1+5 = 6 استعلامات لقاعدة البيانات. الحل لهي المشكلة هو نمط برمجي اسمه DataLoader. فكرته بسيطة: هو بيجمع كل طلبات البيانات المتشابهة خلال دورة حياة الطلب الواحد، وبنفذها مرة واحدة في قاعدة البيانات. بدل 5 استعلامات للتعليقات، بيعمل استعلام واحد: SELECT * FROM comments WHERE post_id IN (1, 2, 3, 4, 5). استخدام DataLoader ضروري جدًا في أي تطبيق GraphQL حقيقي.

الخلاصة: هل أرمي REST API في الزبالة؟

بالتأكيد لا! REST ما زالت تقنية عظيمة وهي أساس الويب الحديث. “كل مطرقة إلها مسمارها”. الفكرة هي إنك توسع صندوق أدواتك 🧰 كمبرمج.

GraphQL أداة قوية جدًا لحل نوع معين من المشاكل، خصوصًا مشاكل الأداء المتعلقة بالشبكة في الواجهات الأمامية المعقدة. هي مش بديل كامل لـ REST، بل هي خيار آخر موجود عندك.

نصيحتي الأخيرة لك: في المرة الجاي اللي بتلاقي حالك بتكتب كود frontend بيعمل سلسلة من طلبات الـ API عشان يعرض واجهة واحدة، تذكر قصة أبو عمر وفنجان القهوة. يمكن يكون هذا هو الوقت المناسب لتبدأ رحلتك مع GraphQL. صدقني، رح تدعيلي. 😉

يعطيكم ألف عافية.

أبو عمر

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

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

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

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

آخر المدونات

الحوسبة السحابية

كل نقرة في لوحة التحكم كانت قنبلة موقوتة: كيف أنقذتني ‘البنية التحتية كشيفرة’ (IaC) من كارثة محققة؟

أشارككم قصة حقيقية عن كارثة كادت أن تدمر مشروعاً كاملاً بسبب نقرة خاطئة في لوحة التحكم السحابية. اكتشفوا كيف أنقذتني منهجية "البنية التحتية كشيفرة" (IaC)...

17 مارس، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

مقابلاتي السلوكية كانت كارثة: كيف أنقذتني طريقة STAR من أسئلة ‘حدثنا عن موقف صعب…؟’

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

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

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

أشارككم قصة حقيقية من قلب المعركة البرمجية، حيث كادت خدمة واحدة بطيئة أن تُسقط نظامنا بالكامل. سأشرح لكم بالتفصيل نمط "قاطع الدائرة" (Circuit Breaker)، وكيف...

16 مارس، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

كنا نخزن بطاقات الائتمان مباشرة… قصة تسريب بيانات وكيف أنقذني الترميز (Tokenization)

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

15 مارس، 2026 قراءة المزيد
أتمتة العمليات

استيقظتُ في الثالثة فجراً لإعادة تشغيل سيرفر: كيف علّمتُ نظامي أن يشفي نفسه بنفسه عبر الأتمتة؟

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

14 مارس، 2026 قراءة المزيد
تسويق رقمي

إعلاناتي كانت تستهدف الجميع… وبالتالي لم تصل لأحد: كيف استخدمتُ نماذج التجزئة (Clustering) لاكتشاف شرائح عملاء لم أكن أعرف بوجودها؟

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

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