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

يا أهلاً وسهلاً فيكم جميعاً، معكم أخوكم أبو عمر.

قبل كم سنة، كنّا شغالين على تطبيق موبايل لمشروع كبير شوي. التطبيق كان فيه واجهات كثيرة، من صفحة المستخدم الشخصية، لقائمة المنتجات، لآخر الأخبار… وشو ما يخطر على بالكم. في البداية، كانت الأمور ماشية زي الحلاوة. لكن مع كل تحديث جديد، كان التطبيق يصير أبطأ وأثقل على الجهاز، والشكاوى من المستخدمين بلشت تزيد: “التطبيق بستهلك كل باقة الإنترنت!”، “ليش بطيء هالقد؟”، “البطارية بتخلص بسرعة!”.

أتذكر تماماً اجتماع يوم خميس، الأجواء كانت مشحونة. فريق الموبايل (الـ Frontend) بحكي لفريق الخوادم (الـ Backend): “يا جماعة الخير، إحنا بدنا بس اسم المستخدم وصورته الشخصية في الهيدر، ليش بتبعتولنا كل تاريخ حياته في الـ API؟ بنستقبل 50 حقل إحنا مش بحاجتهم!”. ورد فريق الـ Backend: “هاي هي الـ endpoint الموجودة (`/api/user/:id`)، ما بنقدر نعمل endpoint جديدة لكل شاشة صغيرة في التطبيق! مش رح نخلص هيك!”.

كانت حرب استنزاف حقيقية. فريق الواجهات الأمامية غارق في بيانات لا يحتاجها (Over-fetching)، مما يسبب بطء التطبيق واستهلاك زائد للبيانات والبطارية. وفريق الواجهات الخلفية منهك من كثرة الطلبات لإنشاء أو تعديل عشرات الـ Endpoints لتناسب كل حالة. شعرنا أننا في طريق مسدود، وكأننا نبني بيتاً وكل طابق فيه يحتاج إلى تصميم سباكة وكهرباء مختلف تماماً. هنا بدأت رحلتنا للبحث عن حل، وهنا تعرفنا على المنقذ: GraphQL.

ما هو جحيم الـ Over-fetching؟ ولماذا كان يقتل تطبيقاتنا؟

قبل ما ندخل في تفاصيل الحل، خلينا نفهم المشكلة اللي كنا واقعين فيها بالضبط. المشكلة اسمها “Over-fetching”، أو “الجلب المفرط للبيانات” بالعربي الفصيح. ببساطة، هي لما تطبيقك يطلب بيانات من الخادم (Server)، والخادم يرجع له بيانات أكثر بكثير من اللي بحتاجها فعلاً.

تخيل أنك تريد عرض قائمة بأسماء مؤلفي المقالات في مدونتك. في نظام REST API التقليدي، قد تضطر إلى عمل طلب مثل هذا:

GET /api/authors

لكن الخادم قد يعيد لك لكل مؤلف معلوماته الكاملة:


[
  {
    "id": "user-1",
    "name": "أبو عمر الفلسطيني",
    "bio": "مبرمج وخبير في الذكاء الاصطناعي...",
    "joinDate": "2010-01-15T10:00:00Z",
    "address": {
      "street": "شارع يافا",
      "city": "القدس"
    },
    "lastLogin": "2024-05-20T12:30:00Z",
    "postsCount": 150
    // ... و 20 حقلاً آخر
  },
  {
    "id": "user-2",
    "name": "مطور آخر",
    // ... ونفس الكم الهائل من البيانات
  }
]

كل ما كنت تحتاجه هو حقل name، لكنك استقبلت حمولة (payload) ضخمة من البيانات غير الضرورية. هذا هو الـ Over-fetching بعينه. هذا الأمر يصبح كارثياً في تطبيقات الموبايل التي تعمل على شبكات إنترنت أبطأ وباقات محدودة.

المشكلة الشقيقة: الـ Under-fetching

على الجانب الآخر، هناك مشكلة الـ “Under-fetching”، وهي عندما لا توفر لك الـ endpoint كل البيانات التي تحتاجها، فتضطر لعمل عدة طلبات (requests) للحصول على المعلومات الكاملة. مثلاً، لعرض مقالة مع اسم كاتبها وتعليقاتها، قد تحتاج إلى 3 طلبات منفصلة في REST:

  1. GET /api/posts/123 لجلب تفاصيل المقالة.
  2. GET /api/users/456 لجلب تفاصيل الكاتب (بعد الحصول على authorId من الطلب الأول).
  3. GET /api/posts/123/comments لجلب التعليقات.

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

دخول GraphQL: اطلب ما تحتاجه فقط!

GraphQL ليست لغة برمجة أو إطار عمل بحد ذاتها، بل هي “لغة استعلام” (Query Language) للـ API الخاصة بك، وبيئة تشغيل في الخادم لتنفيذ هذه الاستعلامات. الفلسفة الأساسية لـ GraphQL بسيطة وعبقرية: “اطلب ما تحتاجه بالضبط، وستحصل على ما طلبته بالضبط”.

بدلاً من وجود عشرات أو مئات الـ endpoints المختلفة في REST، عادة ما يكون لدى GraphQL نقطة نهاية واحدة (single endpoint)، مثل /graphql. العميل (التطبيق) هو الذي يحدد شكل البيانات التي يريدها عن طريق إرسال “استعلام” (Query) يصف الهيكل المطلوب.

مثال عملي: من REST إلى GraphQL

لنتخيل أننا نريد جلب مقالة (Post) مع عنوانها، واسم كاتبها فقط، ونص أول تعليقين فقط.

في عالم REST، كنا سنواجه إما over-fetching (جلب كل بيانات المقالة والكاتب والتعليقات) أو under-fetching (عمل 3 طلبات منفصلة كما ذكرنا سابقاً).

أما في عالم GraphQL، فالعميل يرسل استعلاماً واحداً فقط بهذا الشكل:


query GetPostDetails {
  post(id: "123") {
    title
    author {
      name
    }
    comments(first: 2) {
      body
    }
  }
}

والرد من الخادم سيكون ملف JSON مطابق تماماً لهيكل الطلب، لا زيادة ولا نقصان:


{
  "data": {
    "post": {
      "title": "كيف أنقذنا GraphQL من جحيم الـ Over-fetching؟",
      "author": {
        "name": "أبو عمر الفلسطيني"
      },
      "comments": [
        {
          "body": "مقال رائع ومفيد جداً!"
        },
        {
          "body": "شكراً على الشرح الواضح."
        }
      ]
    }
  }
}

لاحظ الجمال والبساطة! طلب واحد، استجابة واحدة، بيانات دقيقة ومخصصة. هذا يحل مشكلة الـ Over-fetching والـ Under-fetching بضربة واحدة.

كيف يعمل GraphQL؟ نظرة من الداخل

قد تتساءل: كيف يعرف الخادم كيفية جلب هذه البيانات المتشعبة؟ السر يكمن في ثلاثة مكونات رئيسية:

1. الـ Schema (المخطط)

الـ Schema هي قلب أي GraphQL API. إنها بمثابة “عقد” بين العميل والخادم يصف كل البيانات الممكنة التي يمكن للعميل طلبها. يتم كتابة الـ Schema بلغة خاصة (Schema Definition Language – SDL).

مثال بسيط على Schema للمدونة:


type Post {
  id: ID!
  title: String!
  content: String
  author: User!
  comments: [Comment!]
}

type User {
  id: ID!
  name: String!
  email: String
}

type Comment {
  id: ID!
  body: String!
  author: User!
}

# نقطة الدخول الرئيسية للاستعلامات
type Query {
  post(id: ID!): Post
  allPosts: [Post!]
}

هذا المخطط يخبر العميل بالضبط ما هي الحقول والأنواع (Types) المتاحة وكيفية الارتباط بينها. إنه نظام موثق ذاتياً (self-documenting) بشكل رائع.

2. الـ Resolvers (المُحلِّلات)

الـ Schema تصف “ماذا” يمكن طلبه، أما الـ Resolvers فتصف “كيف” يتم جلب هذه البيانات. الـ Resolver هي مجرد دالة (function) مسؤولة عن جلب البيانات لحقل معين في الـ Schema. لكل حقل في مخططك، يمكنك كتابة resolver لجلب بياناته من قاعدة بيانات، أو من API أخرى، أو حتى من ملف ثابت.

3. العمليات (Queries, Mutations, Subscriptions)

  • Queries: لقراءة أو جلب البيانات (كما رأينا في المثال).
  • Mutations: لتعديل البيانات (إنشاء، تحديث، حذف). تشبه طلبات POST/PUT/DELETE في REST.
  • Subscriptions: للحصول على تحديثات في الوقت الفعلي (real-time) باستخدام WebSockets. رائعة لتطبيقات الدردشة أو الإشعارات الفورية.

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

الانتقال إلى GraphQL كان نقلة نوعية لفريقنا، لكن الطريق لم يكن مفروشاً بالورود دائماً. اسمحوا لي أن أشارككم بعض النصائح التي تعلمتها “بالطريقة الصعبة”:

نصيحة #1: ابدأ بالـ Schema أولاً

قبل كتابة أي كود، اجلس مع فريق الواجهات الأمامية (Frontend) وفريق الواجهات الخلفية (Backend) معاً وصمموا الـ Schema. هذا الحوار سيجبر الجميع على التفكير في البيانات التي يحتاجها التطبيق فعلاً، ويوفر عليكم الكثير من التعديلات لاحقاً. الـ Schema هي المصدر الوحيد للحقيقة (Single Source of Truth).

نصيحة #2: لا ترمِ REST API القديمة في القمامة

إذا كان لديك نظام قائم يعتمد على REST APIs، لست مضطراً لإعادة بناء كل شيء من الصفر. “مش كل إشي قديم عاطل يا خالي”. يمكنك بناء طبقة GraphQL فوق واجهات REST الحالية. يمكن للـ Resolvers في GraphQL أن تقوم ببساطة باستدعاء الـ endpoints القديمة وتجميع البيانات. هذه استراتيجية ممتازة للهجرة التدريجية.

نصيحة #3: تعلّم كيفية التعامل مع مشكلة “N+1”

هذه أشهر مشكلة تواجه المبتدئين في GraphQL. تخيل أنك تطلب قائمة من 100 مقالة، ولكل مقالة تريد اسم كاتبها (post { author { name } }). بشكل ساذج، قد يقوم الخادم بتنفيذ استعلام واحد لجلب الـ 100 مقالة، ثم 100 استعلام منفصل لجلب كاتب كل مقالة! (أي 1 + N استعلام). هذا كارثي للأداء.

الحل يكمن في استخدام تقنية اسمها “Batching” (التجميع)، وأشهر مكتبة لتطبيقها هي DataLoader. تقوم هذه المكتبة بتجميع كل طلبات جلب الكتّاب في استعلام واحد فقط (مثلاً: SELECT * FROM users WHERE id IN (id1, id2, ..., id100)). تعلم استخدامها ضروري جداً لأي تطبيق GraphQL حقيقي.

نصيحة #4: فكّر في التخزين المؤقت (Caching)

في REST، كان التخزين المؤقت سهلاً نسبياً على مستوى الـ HTTP لأن كل URL (/api/posts/123) يمثل مورداً فريداً. في GraphQL، كل الطلبات تذهب إلى نفس الـ endpoint (/graphql)، مما يجعل التخزين المؤقت على مستوى الشبكة أصعب. الحل يكون على مستوى العميل باستخدام مكتبات متطورة مثل Apollo Client أو urql، التي توفر نظام تخزين مؤقت ذكي يفهم استعلامات GraphQL ويخزن البيانات بشكل منظم.

الخلاصة: هل GraphQL هي الحل السحري؟

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

بالنسبة لنا، كانت GraphQL هي طوق النجاة الذي أخرجنا من فوضى البيانات والطلبات المتعددة. لقد حسّنت من سرعة التطبيق، وقللت من استهلاك البيانات، والأهم من ذلك، جعلت التعاون بين فريق الـ Frontend والـ Backend أكثر سلاسة وإنتاجية. لم نعد نتجادل حول شكل البيانات، بل أصبحنا نصممها معاً. 🚀

نصيحتي الأخيرة لك: لا تخف من تجربة التقنيات الجديدة. ابدأ بمشروع جانبي صغير، ابنِ GraphQL API بسيطة، وجرّب بنفسك قوة أن تطلب ما تحتاجه فقط. كلنا بلشنا من الصفر، والمهم هو الاستمرار في التعلم والتطور. بالتوفيق يا جماعة!

أبو عمر

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

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

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

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

آخر المدونات

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

كانت فاتورة السحابة تلتهم ميزانيتنا: كيف أنقذتنا ‘الـ FinOps’ من جحيم الإنفاق غير المنضبط؟

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

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

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

بصفتي أبو عمر، مبرمج فلسطيني، أشارككم قصتي مع المشاريع التجريبية التي لا تعود، وكيف أن بناء "ملف إثبات" (Portfolio of Proof) حقيقي كان طوق النجاة...

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

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

في ليلة إطلاق حاسمة، كادت خوادمنا أن تنهار تحت ضغط المستخدمين. أشارككم قصة كيف أنقذتنا تقنية "موازنة الأحمال" من كارثة محققة، وأغوص معكم في تفاصيلها...

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

كانت بياناتنا المالية سجينة: كيف حررتنا واجهات ‘الصيرفة المفتوحة’ من جحيم الصوامع المنعزلة؟

بصفتي أبو عمر، مبرمج فلسطيني، أروي لكم كيف عانينا من سجون البيانات البنكية المنعزلة. سنغوص في عالم "الصيرفة المفتوحة" (Open Banking) لنكتشف كيف حررت واجهات...

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

كانت بنيتنا التحتية قصراً من رمال: كيف أنقذتنا ‘الشيفرة كبنية تحتية’ (IaC) من جحيم التضارب بين البيئات؟

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

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

كانت الأخطاء تُدفن حية: كيف أنقذتنا “السلامة النفسية” من جحيم الفشل الصامت؟

في عالم البرمجة، ليست الأخطاء البرمجية هي ما يقتل المشاريع، بل الأخطاء التي نخشى الاعتراف بها. من خلال قصة حقيقية، نغوص في مفهوم "السلامة النفسية"...

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