كنت أرسل 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. صدقني، رح تدعيلي. 😉

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

أبو عمر

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

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

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

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

آخر المدونات

التوسع والأداء العالي والأحمال

قاعدة بياناتنا كانت تنهار: كيف أنقذنا التخزين المؤقت (Caching) من جحيم الاستعلامات المتكررة؟

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

1 مايو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كانت بنيتنا التحتية قصرًا من ورق: كيف أنقذنا Terraform من جحيم الإعداد اليدوي؟

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

1 مايو، 2026 قراءة المزيد
ادارة الفرق والتنمية البشرية

كان أفضل مهندسينا يرحلون: كيف أنقذ “سلم المسار الوظيفي” شركتنا من جحيم الركود؟

أشارككم قصة حقيقية عن كيفية مواجهتنا لمشكلة "نزيف العقول" في فريقنا الهندسي. نستعرض بالتفصيل كيف قمنا ببناء "سلم مسار وظيفي" (Career Ladder) واضح وشفاف أنقذنا...

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

كان زر النشر يسبب لنا نوبات هلع: كيف أنقذتنا خطوط أنابيب CI/CD من جحيم الإصدارات اليدوية؟

أتذكر ليالي النشر الطويلة المليئة بالتوتر والأخطاء الكارثية. في هذه المقالة، أشارككم قصة تحولنا من الفوضى اليدوية إلى عالم الأتمتة المنظم مع خطوط أنابيب CI/CD،...

1 مايو، 2026 قراءة المزيد
نصائح برمجية

كانت سجلات التغيير لدينا لغزاً: كيف أنقذنا معيار ‘Conventional Commits’ من جحيم ‘git log’ عديم الفائدة؟

أشارككم قصة حقيقية من قلب المعركة البرمجية، كيف انتقلنا من سجلات Git غامضة وفوضوية إلى تاريخ واضح ومنظم باستخدام معيار Conventional Commits. هذه ليست مجرد...

1 مايو، 2026 قراءة المزيد
​معمارية البرمجيات

كان تحديث نظامنا المونوليثي مستحيلاً: كيف أنقذنا نمط ‘التين الخانق’ من جحيم إعادة الكتابة الكارثية؟

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

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