تطبيقاتنا كانت تستجدي البيانات أو تغرق فيها: كيف أنقذنا 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 لم يكن مجرد تغيير تقني، بل كان تغييرًا في ثقافة العمل. لقد أعطى القوة والمرونة لفريق الواجهة الأمامية، وحرر فريق الواجهة الخلفية من طلبات التعديل الصغيرة والمستمرة، والأهم من ذلك، جعل تطبيقاتنا أسرع وأكثر كفاءة. ✨

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

أبو عمر

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

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

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

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

آخر المدونات

تسويق رقمي

كانت حملاتنا تحرق الأموال: كيف أنقذتنا نماذج الإحالة بالبيانات (DDA) من جحيم تخمين العائد على الاستثمار؟

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

25 مايو، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

من فوضى المكونات إلى لغة بصرية موحدة: كيف يبني ‘نظام التصميم’ (Design System) جسراً بين المطورين والمصممين؟

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

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

كان كودنا غارقاً في بحر SQL: كيف أنقذنا ‘الربط الكائني العلائقي’ (ORM) من جحيم الاستعلامات المتكررة؟

أشارككم قصة حقيقية من مسيرتي كمبرمج، عن مشروع كاد أن يغرق في فوضى استعلامات SQL المتكررة. سنكتشف معًا كيف كانت تقنية الربط الكائني العلائقي (ORM)...

25 مايو، 2026 قراءة المزيد
الشبكات والـ APIs

كان كل مايكروسيرفس قلعة منعزلة: كيف أنقذتنا ‘بوابة الواجهات البرمجية’ (API Gateway) من جحيم الفوضى؟

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

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

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

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

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

كان كل طلب يضرب قاعدة البيانات: كيف أنقذنا النظام بـ ‘التخزين المؤقت الموزع’ (Distributed Caching)؟

أشارككم قصة حقيقية عن كيفية انهيار نظام تحت ضغط الطلبات، وكيف كان "التخزين المؤقت الموزع" باستخدام Redis هو طوق النجاة. سنتعمق في المفهوم، ونرى أمثلة...

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

من الإنذار الكاذب إلى الكشف الذكي: كيف أنقذنا نماذج الاحتيال المالي من بحر التنبيهات الخاطئة؟

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

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