تطبيقاتنا كانت تستجدي البيانات أو تغرق فيها: كيف أنقذنا GraphQL من جحيم الـ Over-fetching والـ Under-fetching؟

حكاية “صفحة المستخدم” التي كادت أن تُفشّل المشروع

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

قبل كم سنة، كنت شغال مع فريق على تطبيق اجتماعي جديد، تطبيق طموح وفيه ميزات كثيرة. وصلنا لمرحلة تطوير “صفحة المستخدم” (User Profile). للوهلة الأولى، المهمة تبدو بسيطة: عرض صورة المستخدم، اسمه، نبذة عنه، وآخر 5 منشورات كتبها. لكن هون بلشت المشاكل اللي “بتطلّع الشيب براس الواحد”.

فريق الواجهة الأمامية (Frontend) كان يصرخ: “يا أبو عمر، بدنا نعرض اسم المستخدم وصورته بس في الهيدر، ليش بنضطر نحمّل كل بياناته الشخصية من عنوانه لتاريخ ميلاده في طلب واحد؟ التطبيق بطيء على الإنترنت الضعيف!”. كانوا على حق، هذه المشكلة اسمها Over-fetching، أو “الجلب الزائد للبيانات”. كنا نغرقهم ببيانات لا يحتاجونها.

وبنفس الوقت، عشان يعرضوا آخر 5 منشورات، كانوا يبعثوا طلب يجيب بيانات المستخدم، وبعدها طلب ثاني يجيب قائمة ID المنشورات، وبعدها 5 طلبات منفصلة، طلب لكل منشور عشان يجيبوا تفاصيله! تخيلوا الكارثة؟ 7 طلبات شبكة (Network Requests) عشان نعرض صفحة واحدة! هذه المشكلة اسمها Under-fetching، أو “الجلب الناقص للبيانات”، اللي بتجبرك تعمل رحلات مكوكية للسيرفر. التطبيق كان يستجدي البيانات قطعة قطعة.

الوضع كان متأزم، اجتماعات طويلة، واللوم يترمى بين فريق الواجهة الخلفية (Backend) والواجهة الأمامية. إحنا كـ Backend نقول “اعملوا طلب واحد وخذوا اللي بدكم ياه”، وهم يقولوا “الطلب الواحد فيه جيجات من الداتا ما بدنا إياها!”. شعرنا أننا في جحيم تقني، إلى أن قررنا تجربة حل كان الكل يتكلم عنه وقتها: GraphQL.


ما هو جحيم الـ Over-fetching والـ Under-fetching؟

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

مشكلة الـ Over-fetching: عندما تغرق في البيانات

تخيل أنك ذهبت لمطعم لتطلب كوب ماء، فقام النادل بإحضار بوفيه كامل يتضمن كل ما في المطبخ، ووضع لك الفاتورة كاملة. هذا بالضبط هو الـ Over-fetching في عالم الـ APIs.

باستخدام REST API التقليدي، غالبًا ما يكون لديك نقاط وصول (Endpoints) ثابتة مثل /api/users/123. هذه النقطة قد تُرجع كائن JSON ضخمًا:


{
  "id": 123,
  "name": "أبو عمر",
  "username": "abu_omar_dev",
  "email": "abu.omar@example.com",
  "address": {
    "street": "شارع القدس",
    "city": "نابلس",
    "country": "فلسطين"
  },
  "bio": "مبرمج ومطور برمجيات...",
  "followers_count": 5000,
  "following_count": 150,
  "posts": [ ...قائمة طويلة جدًا من المنشورات... ]
}

الآن، إذا كانت واجهة الموبايل تحتاج فقط لعرض الاسم (name)، فهي مضطرة لتحميل كل هذه البيانات واستهلاك باقة الإنترنت للمستخدم وإبطاء التطبيق بلا أي داعٍ.

مشكلة الـ Under-fetching: عندما تستجدي البيانات

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

باستخدام REST، ستكون الرحلة كالتالي:

  1. الطلب الأول: GET /api/users/123 لجلب بيانات المستخدم.
  2. الطلب الثاني: GET /api/users/123/posts لجلب قائمة منشوراته.
  3. الطلبات التالية (N طلبات): لكل منشور، ستقوم بعمل طلب جديد GET /api/posts/POST_ID/comments لجلب التعليقات.

هذه المشكلة تُعرف بـ “N+1 Problem”. أنت تقوم بطلب واحد رئيسي، ثم N من الطلبات الإضافية. هذا العدد الكبير من رحلات الذهاب والإياب بين العميل والخادم يقتل أداء التطبيق، خصوصًا على شبكات الموبايل غير المستقرة.


GraphQL: المنقذ الذي يمنحك قوة الاختيار

GraphQL ليست لغة برمجة، ولا قاعدة بيانات، ولا إطار عمل (Framework) خاص بالـ Backend. ببساطة، GraphQL هي لغة استعلام (Query Language) للـ API الخاص بك، ومواصفة قياسية لكيفية تنفيذ هذه الاستعلامات.

فكر فيها كأنها “بوفيه مفتوح” بدلًا من “قائمة طعام محددة”. مع REST API، أنت تطلب “الوجبة رقم 5”. مع GraphQL، أنت تذهب للبوفيه وتختار بالضبط ما تريده في صحنك: “أريد القليل من الأرز، قطعة دجاج، والكثير من السلطة، وبدون زيتون”.

كيف يعمل السحر؟

بدلاً من وجود عشرات نقاط الوصول (Endpoints)، في GraphQL عادةً ما يكون لديك نقطة وصول واحدة فقط (مثل /graphql). العميل (الواجهة الأمامية) يرسل “استعلامًا” (Query) على شكل نص يصف البيانات التي يريدها بالضبط.

مثال عملي: حل مشكلة الـ Over-fetching

لنفترض أن الواجهة الأمامية تحتاج فقط لاسم المستخدم وصورته. بدلاً من طلب /api/users/123، سترسل استعلام GraphQL كالتالي:


query GetUserProfileHeader {
  user(id: "123") {
    name
    profilePictureUrl
  }
}

والسيرفر سيرد بـ JSON يحتوي فقط على ما طلبته، لا أكثر ولا أقل:


{
  "data": {
    "user": {
      "name": "أبو عمر",
      "profilePictureUrl": "https://example.com/abu_omar.jpg"
    }
  }
}

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

مثال عملي: حل مشكلة الـ Under-fetching

الآن، لصفحة المستخدم الكاملة التي تحتاج لبيانات المستخدم وآخر 3 منشورات له مع عناوينها، يمكن للواجهة الأمامية كتابة استعلام واحد مركب:


query GetUserProfilePage {
  user(id: "123") {
    name
    bio
    posts(last: 3) {
      id
      title
      createdAt
    }
  }
}

والرد سيأتي في طلب واحد فقط، منظم بنفس شكل الطلب:


{
  "data": {
    "user": {
      "name": "أبو عمر",
      "bio": "مبرمج ومطور برمجيات...",
      "posts": [
        {
          "id": "post-1",
          "title": "مقالتي الجديدة عن GraphQL",
          "createdAt": "2023-10-27T10:00:00Z"
        },
        {
          "id": "post-2",
          "title": "أفضل ممارسات الذكاء الاصطناعي",
          "createdAt": "2023-10-26T15:30:00Z"
        },
        {
          "id": "post-3",
          "title": "كيف تبدأ في البرمجة",
          "createdAt": "2023-10-25T12:00:00Z"
        }
      ]
    }
  }
}

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


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

تبني GraphQL كان نقلة نوعية في مشاريعنا، لكن الطريق ما كان مفروش بالورود. إليكم بعض النصائح من قلب التجربة:

1. ابدأ بالـ Schema: عقدك المقدس

كل شيء في GraphQL يبدأ بالـ Schema (المخطط). هو العقد الموثق بين الواجهة الأمامية والخلفية. استثمر وقتًا في تصميمه جيدًا. استخدم لغة GraphQL Schema Definition Language (SDL) لتعريف أنواع البيانات المتاحة (Types) والاستعلامات (Queries) والتعديلات (Mutations).

نصيحة شخصية: لا تخف من تعديل الـ Schema. GraphQL مصمم للتطور. بدلاً من إصدار v2 و v3 من الـ API كما في REST، يمكنك ببساطة إضافة حقول جديدة دون التأثير على العملاء القدامى. يمكن إخفاء الحقول القديمة باستخدام @deprecated.

2. الـ Resolvers هي دماغ العملية

الـ Schema يصف “ماذا” يمكنك أن تطلب، أما الـ Resolvers فهي الدوال التي تجلب البيانات فعليًا، وتجيب على سؤال “كيف”. كل حقل في الـ Schema الخاص بك يقابله Resolver في الكود الخلفي.

هذا يمنحك مرونة هائلة. الـ Resolver الخاص ببيانات المستخدم قد يجلبها من قاعدة بيانات PostgreSQL، بينما الـ Resolver الخاص بمنشوراته قد يجلبها من MongoDB، والـ Resolver الخاص بالطقس قد يستدعي API خارجي آخر. كل هذا يحدث بسلاسة خلف الكواليس.

3. لا تهمل جانب الأمان والأداء

القوة الكبيرة تأتي مع مسؤولية كبيرة. بما أن العميل يمكنه طلب استعلامات معقدة جدًا، قد يستغل أحدهم هذه الميزة لإرسال استعلامات تستهلك كل موارد السيرفر (Denial of Service).

  • تحديد عمق الاستعلام (Query Depth Limiting): امنع الاستعلامات المتشعبة جدًا (مثل طلب أصدقاء أصدقاء أصدقاء…).
  • تحليل تكلفة الاستعلام (Query Cost Analysis): أعطِ “تكلفة” لكل حقل، وامنع تنفيذ الاستعلامات التي تتجاوز تكلفتها حدًا معينًا.

  • استخدام Timeouts: لا تسمح لاستعلام واحد بالعمل إلى الأبد.

4. عالم الأدوات رائع، فاستغله

النظام البيئي لـ GraphQL ضخم وناضج. لا تحاول إعادة اختراع العجلة.

  • للـ Backend: استخدم مكتبات مثل Apollo Server (لـ Node.js) أو Strawberry (لـ Python) أو graphql-java (لـ Java). هذه المكتبات توفر لك الكثير من الميزات الجاهزة.
  • للـ Frontend: استخدم عملاء مثل Apollo Client أو Relay. هذه المكتبات تسهل إدارة الحالة، التخزين المؤقت (Caching)، والتحديثات الفورية للواجهة. “ما بتغلب حالك، كل شي جاهز ومستنياك”.

الخلاصة: هل يجب أن أستخدم GraphQL؟

GraphQL ليس الحل السحري لكل المشاكل، و REST API ما زال خيارًا ممتازًا للكثير من الحالات البسيطة. لكن، إذا وجدت نفسك في أحد هذه المواقف، فأنصحك بشدة أن تعطي GraphQL فرصة:

  • لديك تطبيقات متعددة (ويب، موبايل iOS، موبايل Android) ولكل منها متطلبات بيانات مختلفة.
  • واجهات تطبيقك معقدة وتحتاج لبيانات من مصادر متعددة في شاشة واحدة.
  • فريق الواجهة الأمامية يشتكي باستمرار من بطء الـ API أو من حاجته لتعديلات مستمرة في الـ Backend.
  • تريد تقليل عدد رحلات الشبكة لتحسين أداء التطبيق على الإنترنت البطيء.

بالنسبة لنا، الانتقال لـ GraphQL لم يكن مجرد تغيير تقني، بل كان تغييرًا في ثقافة العمل. لقد أعطى القوة والمرونة لفريق الواجهة الأمامية، وحرر فريق الواجهة الخلفية من طلبات التعديل الصغيرة والمستمرة، والأهم من ذلك، جعل تطبيقاتنا أسرع وأكثر كفاءة. ✨

فيا صديقي المبرمج، لا تخف من تجربة الجديد. أحيانًا، تغيير بسيط في “كيف” تطلب البيانات، يمكن أن يصنع فرقًا كبيرًا في “ماذا” يمكنك أن تبني. يلا، شدّوا حيلكم! 💪

أبو عمر

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

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

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

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

آخر المدونات

برمجة وقواعد بيانات

استعلاماتنا كانت تزحف كالسلحفاة: كيف أنقذتنا ‘فهرسة قواعد البيانات’ من جحيم البحث الشامل؟

أتذكر ليلة كاد فيها تطبيقنا أن ينهار بسبب بطء قاتل، والسبب؟ استعلام بسيط يستغرق دهراً. في هذه المقالة، أشارككم قصة كيف اكتشفنا عدو الأداء الصامت...

17 أبريل، 2026 قراءة المزيد
الحوسبة السحابية

بنيتنا التحتية كانت مدينة أشباح مكلفة: كيف أنقذتنا ‘الحوسبة بدون خوادم’ (Serverless) من جحيم السيرفرات الخاملة؟

بنيتنا التحتية كانت كمدينة أشباح، سيرفرات تعمل 24/7 بتكاليف باهظة واستخدام شبه معدوم. في هذه المقالة، أشارككم يا جماعة قصتنا مع الحوسبة بدون خوادم (Serverless)...

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

سيرتي الذاتية كانت مجرد حبر على ورق: كيف أنقذتني ‘المساهمات في المشاريع المفتوحة المصدر’ من جحيم إثبات مهاراتي؟

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

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

تطبيقنا كان ينهار في أوقات الذروة: كيف أنقذتنا ‘موازنة الأحمال’ (Load Balancing) من جحيم فشل السيرفر الواحد؟

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

17 أبريل، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

رحلة التحقق من الهوية: كيف أنقذنا الذكاء الاصطناعي من جحيم التسجيل اليدوي في عالم الـFintech

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

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

مقابلاتنا الفردية كانت استجوابًا: كيف أنقذتنا ‘الأجندة التعاونية’ من جحيم اللقاءات عديمة الجدوى؟

أشارككم تجربتي كقائد فريق تقني، وكيف حولت الاجتماعات الفردية (One-on-Ones) من جلسات استجواب مملة إلى محادثات مثمرة وبناءة باستخدام أداة بسيطة وفعالة: الأجندة التعاونية. اكتشف...

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

اختباراتنا كانت خضراء والكود مليء بالثغرات: كيف أنقذنا ‘الالاختبار الطفري’ من جحيم الثقة الزائفة؟

أشارككم قصة حقيقية حول كيف خدعتنا نسبة تغطية الاختبارات (Test Coverage) التي بلغت 100%، وكيف كان "الاختبار الطفري" (Mutation Testing) هو البطل الذي كشف ضعف...

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