واجهاتنا كانت تطلب بيانات لا تحتاجها: كيف أنقذنا GraphQL من جحيم الاستدعاءات المتعددة والبيانات الزائدة؟

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

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

لتحميل هذه الواجهة البسيطة، كان تطبيق الموبايل المسكين مضطراً للقيام بثلاثة استدعاءات منفصلة لواجهة برمجة التطبيقات (API) من نوع REST:

  1. GET /api/v1/users/me لجلب معلومات المستخدم.
  2. GET /api/v1/products?sort=newest&limit=5 لجلب أحدث المنتجات.
  3. GET /api/v1/users/me/orders?limit=2 لجلب آخر طلبين.

المصيبة ما كانت بس في عدد الطلبات، المصيبة الأكبر كانت في حجم البيانات. الاستدعاء الأول /api/v1/users/me كان يرجع كائن JSON ضخم فيه كل شيء عن المستخدم: اسمه، إيميله، عنوانه الكامل، تاريخ ميلاده، سجل طلباته بالكامل، قائمة أمنياته… كل شيء! بينما كل ما كنا نحتاجه في الواجهة الرئيسية هو الاسم وصورة البروفايل. كنا بنحمّل “شوال” بيانات عشان نستخدم منه “حبتين” بس. وعلى شبكات الموبايل البطيئة، كانت النتيجة كارثية: بطء في التحميل وتجربة مستخدم سيئة جدًا. هنا كانت البداية، بداية رحلتنا للبحث عن المنقذ: GraphQL.

ما هو جحيم الـ REST API الذي كنا نعيشه؟

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

مشكلة البيانات الزائدة (Over-fetching)

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


{
  "id": "user-123",
  "name": "أبو عمر",
  "email": "abu.omar@example.com",
  "avatarUrl": "https://example.com/avatar.jpg",
  "dateOfBirth": "1980-01-15T00:00:00.000Z",
  "address": {
    "street": "شارع القدس",
    "city": "رام الله",
    "country": "فلسطين",
    "zipCode": "P602"
  },
  "fullOrderHistory": [
    { "orderId": "order-abc", "total": 99.99, "date": "..." },
    { "orderId": "order-def", "total": 45.50, "date": "..." },
    // ... والمزيد من الطلبات
  ],
  "wishlist": [
    // ... قائمة طويلة من المنتجات
  ]
}

كل هذه البيانات الإضافية تستهلك من باقة الإنترنت للمستخدم، تزيد من وقت التحميل، وتضع عبئًا غير ضروري على السيرفر والشبكة. إنها هدر حقيقي للموارد.

مشكلة نقص البيانات والاستدعاءات المتعددة (Under-fetching)

هذه هي الوجه الآخر للعملة. الـ Under-fetching يعني أن نقطة النهاية (Endpoint) الواحدة لا توفر كل البيانات التي تحتاجها، مما يجبرك على إجراء استدعاءات متعددة لتجميع كل المعلومات اللازمة لعرض واجهة واحدة.

وهذا بالضبط ما كان يحدث معنا. لعرض الشاشة الرئيسية، كنا بحاجة إلى:

  1. بيانات المستخدم (استدعاء 1).
  2. بيانات المنتجات (استدعاء 2).
  3. بيانات الطلبات (استدعاء 3).

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

نصيحة من أبو عمر: إذا وجدت نفسك كمطور واجهة أمامية (Frontend) تقوم بعمل “chain” لعدة استدعاءات API لعرض صفحة واحدة، أو إذا كنت تستقبل كائنات ضخمة وتستخدم 10% منها فقط، فهذه علامة حمراء كبيرة على أن بنية الـ API لديك بحاجة إلى إعادة نظر.

GraphQL: المنقذ الذي أتى من فيسبوك

بعد ما “فاض الكيل”، بدأنا نبحث عن بدائل. سمعنا عن تقنية تستخدمها فيسبوك اسمها GraphQL، وبعد القراءة والبحث، أدركنا أنها قد تكون الحل الأمثل لمشاكلنا.

ما هي GraphQL بالضبط؟

GraphQL هي لغة استعلام (Query Language) لواجهات برمجة التطبيقات، بالإضافة إلى كونها بيئة تشغيل (Runtime) لتنفيذ هذه الاستعلامات باستخدام بياناتك الحالية. الفكرة الثورية فيها هي تحويل ميزان القوة من السيرفر إلى العميل.

فكر فيها بهذه الطريقة:

  • REST API (مثل البوفيه المفتوح): السيرفر يقرر شكل “الطبق” (البيانات). تذهب إلى نقطة النهاية /users/1، والسيرفر يعطيك طبقًا مليئًا بكل ما يعرفه عن المستخدم رقم 1، سواء أردت كل هذه المعلومات أم لا.
  • GraphQL API (مثل قائمة الطعام الانتقائية): العميل (أنت) هو من يقرر ماذا يريد في طبقه. أنت ترسل قائمة دقيقة بما تحتاجه (“أريد الاسم وصورة البروفايل للمستخدم رقم 1، واسم وسعر أول 5 منتجات جديدة”)، والسيرفر يجهز لك هذا الطبق المخصص ويرسله لك. لا زيادة ولا نقصان.

بهذه الطريقة، GraphQL تحل المشكلتين الرئيسيتين بضربة واحدة:

  1. لا يوجد Over-fetching: لأنك تطلب فقط ما تحتاجه.
  2. لا يوجد Under-fetching: لأنك تستطيع طلب كل البيانات التي تحتاجها من مصادر مختلفة (مستخدمين، منتجات، طلبات) في استدعاء واحد فقط.

من النظرية إلى التطبيق: كيف بنينا أول واجهة GraphQL

الكلام النظري جميل، لكن “الشغل” هو المحك الحقيقي. خلونا نشوف كيف طبقنا هذا بشكل عملي.

الخطوة الأولى: تعريف المخطط (Schema)

أول شيء في GraphQL هو بناء الـ “Schema”. وهو بمثابة العقد أو الدستور الذي يصف كل البيانات التي يمكن للعميل طلبها وأنواعها. إنه يوثق الـ API بشكل صارم. كتبنا الـ Schema الخاص بنا باستخدام لغة تعريف المخطط (SDL):


# يصف شكل بيانات المستخدم
type User {
  id: ID!
  name: String
  avatarUrl: String
  orders(limit: Int): [Order] # يمكن جلب طلبات المستخدم أيضًا
}

# يصف شكل بيانات المنتج
type Product {
  id: ID!
  name: String
  price: Float
  imageUrl: String
  description: String
}

# يصف شكل بيانات الطلب
type Order {
  id: ID!
  date: String
  total: Float
  items: [Product]
}

# هذه هي نقطة الدخول الرئيسية للاستعلامات
type Query {
  user(id: ID!): User
  latestProducts(limit: Int): [Product]
}

هذا المخطط يخبر أي مطور بالضبط ما هي البيانات المتاحة وكيفية طلبها. لا مزيد من التخمين أو قراءة وثائق قديمة.

الخطوة الثانية: كتابة الاستعلام من جهة العميل

الآن، بدلًا من ثلاثة استدعاءات، أصبح تطبيق الموبايل يرسل استعلامًا واحدًا فقط إلى نقطة نهاية GraphQL الوحيدة (عادة /graphql). وهذا الاستعلام يصف بالضبط البيانات المطلوبة للواجهة الرئيسية:


query GetHomePageData {
  # اطلب المستخدم الحالي
  user(id: "me") {
    name
    avatarUrl
  }
  # اطلب أحدث 5 منتجات
  latestProducts(limit: 5) {
    name
    imageUrl
    price
  }
  # اطلب آخر طلبين للمستخدم الحالي
  # لاحظ كيف يمكننا طلب بيانات مرتبطة داخل نفس الاستعلام
  userOrders: user(id: "me") {
    orders(limit: 2) {
      id
      total
    }
  }
}

انظروا لجمال وبساطة هذا الاستعلام. إنه يقرأ كأنه وصف للبيانات التي نريدها. طلبنا اسم المستخدم وصورته، واسم وصورة وسعر المنتجات، ورقم وتكلفة آخر طلبين، كل ذلك في طلب واحد!

الخطوة الثالثة: النتيجة السحرية (الاستجابة)

السيرفر الذي يشغل GraphQL يستقبل هذا الاستعلام، ويقوم بتجميع البيانات من المصادر المختلفة (قاعدة البيانات، خدمات أخرى)، ثم يعيد استجابة JSON شكلها يطابق تمامًا شكل الاستعلام:


{
  "data": {
    "user": {
      "name": "أبو عمر",
      "avatarUrl": "https://example.com/avatar.jpg"
    },
    "latestProducts": [
      {
        "name": "كنافة نابلسية",
        "imageUrl": "https://example.com/knafeh.jpg",
        "price": 10.0
      },
      {
        "name": "زيت زيتون بكر",
        "imageUrl": "https://example.com/olive-oil.jpg",
        "price": 15.0
      }
      // ... 3 منتجات أخرى
    ],
    "userOrders": {
      "orders": [
        { "id": "order-xyz", "total": 25.0 },
        { "id": "order-pqr", "total": 50.0 }
      ]
    }
  }
}

طلب واحد، استجابة واحدة، تحتوي بالضبط على ما نحتاجه. لا بايت واحد ضائع. كانت النتيجة فورية: الواجهة الرئيسية للتطبيق أصبحت تُحمّل بسرعة البرق. شعور لا يوصف بالرضا! 🚀

GraphQL ليست الحل السحري لكل شيء

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

متى قد لا تكون GraphQL الخيار الأمثل؟

  • للتطبيقات البسيطة جدًا: إذا كان لديك API صغير جدًا مع عدد قليل من نقاط النهاية الثابتة وعميل واحد فقط (مثل لوحة تحكم داخلية بسيطة)، قد تكون REST API أسرع في التنفيذ وأسهل في الإدارة.
  • التخزين المؤقت (Caching): التخزين المؤقت على مستوى HTTP أسهل بكثير مع REST. يمكنك تخزين استجابة GET /api/products بأكملها. مع GraphQL، كل استعلام قد يكون فريدًا، مما يجعل التخزين على مستوى الشبكة أصعب. الحل يكون عادة في التخزين على مستوى العميل (باستخدام مكتبات مثل Apollo Client).
  • رفع الملفات: رفع الملفات ليس جزءًا أساسيًا من مواصفات GraphQL. يتطلب حلولًا إضافية (مثل استخدام multipart request)، بينما هو أمر مباشر جدًا في REST.

نصيحتي: فكر في “العميل” أولاً

القرار بين REST و GraphQL يجب أن يعتمد على احتياجات “العملاء” الذين سيستهلكون الـ API.

  • هل لديك عدة عملاء مختلفين (تطبيق موبايل، موقع ويب، تطبيق ديسكتوب) باحتياجات بيانات مختلفة؟ GraphQL هو صديقك المفضل.
  • هل واجهاتك الأمامية تتطور بسرعة وتحتاج إلى مرونة في طلب البيانات دون انتظار تحديثات من فريق الـ Backend؟ GraphQL ستمنحك هذه القوة.
  • هل لديك API بسيط ومستقر يخدم غرضًا واحدًا محددًا؟ ربما REST كافٍ ويفي بالغرض.

الخلاصة: استرجع السيطرة على بياناتك 💡

كانت رحلتنا مع GraphQL بمثابة نقلة نوعية في طريقة تفكيرنا ببناء الواجهات البرمجية. لقد حررتنا من قيود الـ REST API التقليدية، وأعطت مطوري الواجهات الأمامية (Frontend) القوة والمرونة التي كانوا يحتاجونها لبناء تجارب مستخدم سريعة وفعالة.

الانتقال إلى GraphQL حل لنا مشاكل حقيقية تتعلق بالأداء واستهلاك الموارد، وقلل من الاعتمادية بين فريق الـ Frontend والـ Backend. لم نعد نسمع جملة “نحتاج نقطة نهاية جديدة” لكل تغيير بسيط في الواجهة.

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

ويلا، شدوا حيلكم يا شباب! 💪

أبو عمر

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

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

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

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

آخر المدونات

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

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

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

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

كانت إجاباتي في المقابلات عشوائية: كيف أنقذتني منهجية STAR من جحيم أسئلة “حدثنا عن موقف…”؟

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

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

كيف أنقذ ‘موازن الحمل’ خادمنا الوحيد من الانهيار؟ قصة من قلب المعركة

هل يواجه تطبيقك بطئًا وتوقفًا مفاجئًا مع زيادة عدد المستخدمين؟ في هذه المقالة، أشارككم قصتي مع انهيار خادمنا الوحيد وكيف كان 'موازن الحمل' (Load Balancer)...

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

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

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

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

وداعاً لـ `kubectl apply -f`: كيف حولنا إدارة Kubernetes إلى عملية آلية وموثوقة مع GitOps؟

في هذه المقالة، يشارككم أبو عمر، مطور برمجيات فلسطيني، قصة حقيقية حول مخاطر الإدارة اليدوية لـ Kubernetes وكيف أنقذنا مبدأ GitOps من كوارث محتملة. سنتعمق...

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

كانت الأفكار تموت في صمت: كيف أنقذتنا ‘السلامة النفسية’ من جحيم الخوف من الفشل؟

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

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