واجهاتنا البرمجية كانت إما ثرثارة أو بخيلة: كيف أنقذنا GraphQL من جحيم الـ Over/Under-fetching؟

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

كنا في رام الله، فريق صغير متحمس، شغالين على تطبيق موبايل فكرته بسيطة وحلوة: دليل للمطاعم والمقاهي المحلية مع تقييمات المستخدمين. التطبيق كان شكله جميل على الورق وفي التصاميم، لكن لما بلّشنا نربطه مع الواجهة الخلفية (Backend)، بدأت الكوابيس. التطبيق كان بطيئاً جداً، الصفحة الرئيسية اللي بتعرض قائمة المطاعم كانت تحتاج وقت طويل للتحميل، لدرجة إن المستخدم ممكن يزهق ويغلق التطبيق.

بعد ليالي من فحص الكود وتتبع الشبكة، اكتشفنا المصيبة. زميلنا في الواجهة الخلفية، بحسن نية، عمل النا endpoint واحد اسمه /restaurants. هذا المسكين كان كريم زيادة عن اللزوم، لما نطلبه عشان نعرض بس اسم وصورة المطعم في القائمة، كان يرجع النا كل شي بيعرفه عن كل مطعم: الاسم، الصورة، العنوان، كل قائمة الطعام بأسعارها، كل تقييمات المستخدمين بنصوصها الكاملة، وتاريخ افتتاح المطعم! كانت واجهة برمجية “ثرثارة” بكل معنى الكلمة. تطبيق الموبايل المسكين كان يستقبل ميغابايتات من البيانات وهو كل اللي محتاجه بضعة كيلوبايتات. هذه المشكلة، يا جماعة، اسمها Over-fetching.

ولما حاولنا نحل المشكلة، وقعنا في الفخ المعاكس. طلبنا من زميلنا “يخفف حكي” شوي، فعمل النا endpoint جديد بيرجع بس اسم المطعم والـ ID تبعه. صار التطبيق سريع، لكن لما المستخدم يضغط على مطعم معين عشان يشوف تفاصيله، كنا نوقع في جحيم آخر. نضطر نبعت طلب عشان نجيب تفاصيل المطعم، وبعدها طلب ثاني عشان نجيب قائمة الطعام، وطلبات إضافية لكل مجموعة من التقييمات! صرنا نبعت 5-6 طلبات بس عشان نفتح صفحة واحدة. هذه المشكلة اسمها Under-fetching، والواجهة هون صارت “بخيلة”.

بين الواجهة الثرثارة والواجهة البخيلة، ضاعت طاستنا وكدنا نفقد الأمل. إلى أن سمعت عن تقنية جديدة وقتها اسمها GraphQL، كانت بمثابة الضوء في آخر النفق المظلم. خلوني أحكيلكم كيف هالشغلة غيرت كل طريقة تفكيرنا في بناء الـ APIs.

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

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

المشكلة الأولى: الثرثرة (Over-fetching)

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

تخيل أن لديك endpoint في REST API اسمه /api/posts لجلب قائمة المقالات في مدونة. الصفحة الرئيسية لتطبيقك تحتاج فقط عنوان كل مقال وتاريخ نشره. ولكن الـ endpoint مصمم ليرجع كل شيء:


// GET /api/posts
[
  {
    "id": 1,
    "title": "مقالتي الأولى",
    "content": "هذا هو محتوى المقال الأول كاملاً...", // بيانات ضخمة لا أحتاجها الآن
    "author": {
      "id": 101,
      "name": "أبو عمر",
      "bio": "مبرمج فلسطيني يحب القهوة والكود..." // بيانات إضافية لا أحتاجها
    },
    "comments": [ ... ] // قائمة طويلة من التعليقات لا أحتاجها
  },
  // ... والمزيد من المقالات بنفس الشكل
]

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

المشكلة الثانية: البُخل (Under-fetching)

هذه هي النقيض تماماً. تحدث عندما لا يوفر endpoint واحد كل البيانات التي يحتاجها العميل، مما يضطره إلى إرسال طلبات متعددة (Multiple Round Trips) إلى الخادم للحصول على كل المعلومات.

باستخدام نفس مثال المدونة، تخيل أنك تريد عرض صفحة مقال واحد مع اسم الكاتب وصورته. الـ endpoint /api/posts/1 قد يعطيك هذا:


// GET /api/posts/1
{
  "id": 1,
  "title": "مقالتي الأولى",
  "content": "هذا هو محتوى المقال...",
  "authorId": 101 // فقط معرّف الكاتب!
}

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


// GET /api/users/101
{
  "id": 101,
  "name": "أبو عمر",
  "avatar": "url_to_image.jpg"
}

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

الحل السحري: تعرف على GraphQL

GraphQL ليست مكتبة، ولا إطار عمل، وليست مرتبطة بقاعدة بيانات معينة. هي ببساطة لغة استعلام (Query Language) للـ API، ومواصفة لتشغيل هذه الاستعلامات. تم تطويرها داخلياً في فيسبوك عام 2012 لحل مشاكل تطبيقات الموبايل المعقدة، وأصبحت مفتوحة المصدر في 2015.

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

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

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

يعتمد GraphQL على ثلاثة مفاهيم رئيسية:

  1. الـ Schema (المخطط): هو العقد بين العميل والخادم. يتم تعريفه في الواجهة الخلفية باستخدام لغة تعريف المخطط (Schema Definition Language – SDL). يصف هذا المخطط كل أنواع البيانات المتاحة والعمليات التي يمكن للعميل إجراؤها.
  2. الـ Query (الاستعلام): هو ما يرسله العميل ليطلب البيانات. شكله يشبه JSON ولكن بدون قيم.
  3. الـ Resolver (المُحلِّل): هي الدوال الموجودة على الخادم والمسؤولة عن جلب البيانات لكل حقل في الـ Schema. يمكن لهذه الدوال أن تجلب البيانات من قاعدة بيانات، أو من REST API آخر، أو من أي مصدر بيانات تتخيله.

لنحل مشاكلنا القديمة باستخدام GraphQL

دعنا نعد إلى مثال المدونة ونرى كيف ستحل GraphQL مشاكل الثرثرة والبخل.

1. تعريف الـ Schema (العقد)

أولاً، سنقوم بتعريف أنواع البيانات التي لدينا في الواجهة الخلفية:


type Post {
  id: ID!
  title: String!
  content: String
  author: User!
}

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

# هذا هو المدخل الرئيسي للبيانات
type Query {
  posts: [Post]
  post(id: ID!): Post
}

لاحظ كيف أن المخطط يصف البيانات والعلاقات بينها (المقال Post له كاتب User). هذا المخطط هو مصدر الحقيقة الوحيد لواجهة الـ API.

2. حل مشكلة الـ Over-fetching (الثرثرة)

تتذكر كيف كانت الصفحة الرئيسية لتطبيقنا بطيئة لأنها تستقبل بيانات لا تحتاجها؟ الآن، يمكن للعميل (تطبيق الموبايل) أن يرسل الاستعلام التالي:


query GetPostTitlesForHomepage {
  posts {
    id
    title
  }
}

سيقوم الخادم بالرد بـ JSON يطابق تماماً شكل الطلب، لا أكثر ولا أقل:


{
  "data": {
    "posts": [
      {
        "id": "1",
        "title": "مقالتي الأولى"
      },
      {
        "id": "2",
        "title": "مقالتي الثانية"
      }
    ]
  }
}

بوم! استلمنا فقط ما نحتاجه. لا محتوى، لا تفاصيل كاتب، لا تعليقات. التطبيق الآن سريع وخفيف.

3. حل مشكلة الـ Under-fetching (البُخل)

والآن، ماذا عن صفحة تفاصيل المقال التي كانت تتطلب طلبات متعددة؟ مع GraphQL، يمكن للعميل طلب كل ما يحتاجه في طلب واحد فقط:


query GetSinglePostWithAuthorDetails {
  post(id: "1") {
    id
    title
    content
    author {
      name
      avatar
    }
  }
}

وسيكون الرد من الخادم بهذا الشكل الجميل:


{
  "data": {
    "post": {
      "id": "1",
      "title": "مقالتي الأولى",
      "content": "هذا هو محتوى المقال...",
      "author": {
        "name": "أبو عمر",
        "avatar": "url_to_image.jpg"
      }
    }
  }
}

هل ترى الجمال؟ حصلنا على بيانات المقال وبيانات الكاتب المرتبط به في طلب واحد، وقمنا بتحديد الحقول التي نريدها من كل منهما. لا مزيد من شلالات الطلبات!

نصائح من مطبخ أبو عمر 🍳

بعد العمل على GraphQL في عدة مشاريع، تعلمت بعض الدروس التي أحب أن أشاركها معكم.

متى تستخدم GraphQL؟

GraphQL ليست الرصاصة الفضية التي تحل كل المشاكل. هي أداة قوية، لكن استخدامها يعتمد على الموقف. هي ممتازة جداً في الحالات التالية:

  • تطبيقات الموبايل: حيث أن كفاءة استهلاك الشبكة أمر حاسم.
  • الواجهات الأمامية المعقدة (SPAs): عندما يكون لديك واجهات مستخدم تحتاج بيانات من مصادر متعددة في شاشة واحدة (مثل لوحات التحكم).
  • بوابة للخدمات المصغرة (Microservices Gateway): إذا كان لديك عشرات الخدمات المصغرة، يمكن لـ GraphQL أن تعمل كواجهة موحدة تجمع البيانات منها وتقدمها للعميل بطريقة بسيطة.

أما إذا كنت تبني API بسيطة جداً، مثل CRUD API لمورد واحد، فقد تكون REST API التقليدية كافية وأبسط في التنفيذ.

احذر من فخ “N+1 Problem”

هذه أشهر مشكلة قد تواجهك عند بناء خادم GraphQL. تخيل أن لديك استعلاماً يطلب كل المقالات مع مؤلفيها. قد يقوم الخادم بتنفيذ استعلام لقاعدة البيانات لجلب كل المقالات (1 query)، ثم لكل مقال، يقوم بتنفيذ استعلام منفصل لجلب مؤلفه (N queries). المجموع هو N+1 استعلام، وهذا كارثي للأداء.

الحل يكمن في استخدام تقنية اسمها “Batching and Caching”. أشهر مكتبة لحل هذه المشكلة هي DataLoader (من فيسبوك أيضاً). فكرتها بسيطة: هي تجمع كل الـ IDs التي تحتاجها (مثلاً كل `authorId` من المقالات)، ثم تنفذ استعلاماً واحداً فقط لجلب كل المؤلفين المطلوبين دفعة واحدة (مثلاً SELECT * FROM users WHERE id IN (101, 102, 105, ...)). تعلم استخدامها، فهي ضرورية جداً.

لا تهدم بيتك القديم!

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

الخلاصة يا جماعة الخير

الانتقال من REST إلى GraphQL كان نقلة نوعية في طريقة تفكيرنا. لقد أعطى القوة للواجهة الأمامية، وسمح لنا ببناء تطبيقات أسرع وأكثر كفاءة، وقلل من سوء التفاهم بين فريق الواجهة الأمامية والخلفية. لم نعد نتجادل حول “ماذا يجب أن يرجع الـ endpoint؟”، بل أصبح العميل هو من يقرر.

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

أبو عمر

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

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

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

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

آخر المدونات

​معمارية البرمجيات

خدماتنا كانت في علاقة سامة: كيف أنقذتنا ‘المعمارية القائمة على الأحداث’ (EDA) من جحيم الاقتران الخانق؟

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

13 أبريل، 2026 قراءة المزيد
ذكاء اصطناعي

نماذجنا اللغوية كانت تهلوس: كيف أنقذنا التوليد المعزز بالاسترجاع (RAG) من جحيم المعلومات الخاطئة؟

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

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

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

بتذكر مرة كُنا نبني لوحة تحكم معقدة، وصارت زي قمرة قيادة طائرة حربية من كثرة الأزرار والمؤشرات. في هذه المقالة، بحكي لكم كيف اكتشفنا مفهوم...

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

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

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

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

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

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

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

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

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

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