واجهات REST كانت تجبرنا على رحلات متعددة: كيف أنقذنا GraphQL من جحيم الـ Over-fetching والـ Under-fetching؟

يا جماعة الخير، السلام عليكم ورحمة الله.

بتذكر قبل كم سنة، يمكن في 2016 أو 2017، كنّا قاعدين في المكتب بنشتغل على تطبيق موبايل لمطعم مشهور. تطبيق بسيط: يعرض قائمة الأكل، ملف المستخدم، وطلباته السابقة. وقتها، كان كل شغلنا على واجهات REST. كانت هي “الأصول” وما كان في بديل معروف بنفس القوة.

المشكلة بلشت تظهر مع أول شاشة في التطبيق، الشاشة الرئيسية. عشان نعرضها، كنا محتاجين: معلومات المستخدم اللي مسجل دخوله (اسمه وصورته)، قائمة بأجدد 3 عروض، وعدد الأطباق في سلته. حسب منطق REST، هذا يعني 3 طلبات (requests) منفصلة للسيرفر:

  1. GET /api/users/me
  2. GET /api/promotions?limit=3
  3. GET /api/cart/count

كل طلب بروح وبرجع لحاله. على شبكة واي فاي سريعة في المكتب، الأمور “ماشي حالها”. لكن لما نجرّب التطبيق على شبكة 3G ضعيفة، كانت المصيبة. الشاشة بتضلها بيضا لوقت طويل، والعناصر بتظهر بالتقسيط: أول إشي بيظهر اسم المستخدم، بعد ثانيتين بتظهر العروض، وبعدين بيظهر رقم صغير فوق أيقونة السلة. تجربة المستخدم كانت سيئة جداً، وكنا حاسين حالنا مكتفين ومش عارفين نحل المشكلة من جذورها.

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

ما هي واجهة REST وليش “كنّا” نحبها؟

قبل ما نهاجم REST، لازم نعطيها حقها. هندسة REST (Representational State Transfer) كانت ثورة في وقتها، وهي مبنية على مفاهيم بسيطة وواضحة موجودة في الإنترنت نفسه. الفكرة الأساسية هي أن كل شيء عبارة عن “مورد” (Resource)، ولكل مورد عنوان فريد (Endpoint).

على سبيل المثال:

  • للحصول على قائمة المستخدمين: GET /users
  • للحصول على المستخدم رقم 5: GET /users/5
  • لإنشاء مستخدم جديد: POST /users
  • لتحديث المستخدم رقم 5: PUT /users/5

هذا النموذج كان ممتازاً لعدة أسباب:

  • بسيط ومفهوم: سهل التعلم والتطبيق.
  • عديم الحالة (Stateless): كل طلب يحتوي على كل المعلومات اللازمة لتنفيذه، مما يسهل التوسع (Scaling).
  • استخدام معايير الـ HTTP: يعتمد على بروتوكول HTTP المعروف، مما يجعله متوافقاً مع كل شيء تقريباً.

باختصار، REST مش “عاطلة”، بالعكس، هي بنت الأساس القوي اللي وقفنا عليه لسنوات. لكن مع تطور التطبيقات، خصوصاً تطبيقات الموبايل والـ Single Page Applications، بلشت عيوبها تظهر بوضوح.

جحيم الرحلات المتعددة: Over-fetching و Under-fetching

المشكلتان الأساسيتان اللتان واجهناهما مع REST، واللتان كانتا السبب في بطء تطبيق المطعم، هما الـ Over-fetching والـ Under-fetching.

مشكلة الـ Over-fetching: “أعطيني بس الملح، مش كل المطبخ!”

الـ Over-fetching تحدث عندما يُرجع السيرفر بيانات أكثر بكثير مما يحتاجه العميل (Client). في نموذج REST، بنية الاستجابة (Response) يحددها السيرفر بشكل كامل.

مثال عملي:

في تطبيقنا، كنا نحتاج فقط اسم المستخدم وصورته الشخصية لعرضها في أعلى الشاشة. لكن الـ endpoint المسؤول عن جلب معلومات المستخدم GET /api/users/me كان يُرجع كائن JSON ضخم:


{
  "id": 123,
  "username": "abu_omar_dev",
  "firstName": "عمر",
  "lastName": "أحمد",
  "email": "abu.omar@example.com",
  "profilePicture": "https://.../image.png",
  "bio": "مبرمج ومطور برمجيات فلسطيني...",
  "dateOfBirth": "1985-01-15",
  "address": {
    "street": "شارع فلسطين",
    "city": "رام الله",
    "country": "فلسطين"
  },
  "lastLogin": "2023-10-26T10:00:00Z",
  "createdAt": "2015-03-10T12:00:00Z",
  "permissions": ["admin", "editor"],
  // ... والمزيد من الحقول التي لا تلزمنا
}

تطبيق الموبايل كان يحتاج فقط حقلين (firstName و profilePicture)، لكنه كان مجبراً على تحميل واستقبال كل هذه البيانات، مما يؤدي إلى:

  • هدر في استهلاك باقة الإنترنت: مشكلة كبيرة للمستخدمين على الشبكات الخلوية.
  • بطء في التطبيق: يحتاج العميل وقتاً أطول لتحميل البيانات وتحليلها (Parsing).
  • زيادة الحمل على السيرفر: السيرفر يعمل على جلب بيانات من قاعدة البيانات وتهيئتها دون أي داعٍ.

مشكلة الـ Under-fetching: “مشوار لكل شغلة صغيرة”

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

مثال عملي (مشكلة N+1):

لنفترض أننا نريد عرض قائمة مقالات مع اسم كاتب كل مقال. في REST، قد يبدو الأمر هكذا:

الرحلة الأولى: جلب كل المقالات.

GET /api/posts

الاستجابة ستكون مصفوفة من المقالات، كل مقال يحتوي على authorId:


[
  { "id": 1, "title": "مقدمة في GraphQL", "authorId": 10 },
  { "id": 2, "title": "أداء الواجهات البرمجية", "authorId": 15 },
  { "id": 3, "title": "REST مقابل GraphQL", "authorId": 10 }
]

لاحظ أننا حصلنا على “رقم” الكاتب، وليس اسمه. الآن، لعرض اسم الكاتب بجانب كل مقال، نحن مضطرون للقيام برحلات إضافية، رحلة لكل كاتب فريد (أو رحلة لكل مقال في أسوأ الحالات).

الرحلات الإضافية:


GET /api/users/10  // لجلب معلومات كاتب المقال الأول والثالث
GET /api/users/15  // لجلب معلومات كاتب المقال الثاني

إذا كان لدينا 20 مقالاً كتبها 20 كاتباً مختلفاً، فهذا يعني 1 (لجلب المقالات) + 20 (لجلب الكتّاب) = 21 رحلة! هذا هو ما يسمى بمشكلة “N+1 Query”، وهو كابوس حقيقي للأداء.

ظهور المنقذ GraphQL: اطلب ما تحتاجه بالضبط

هنا يأتي دور GraphQL. GraphQL ليست بديلاً كاملاً لـ REST، بل هي لغة استعلام (Query Language) للواجهات البرمجية، وفلسفتها مختلفة تماماً. الفكرة بسيطة وعبقرية: بدلاً من أن يكون للسيرفر عدة نقاط نهاية (endpoints) ثابتة، يوجد لديه نقطة نهاية واحدة ذكية تفهم الطلبات المعقدة.

فكر فيها كأنك ذهبت من بوفيه مفتوح (REST) حيث تضطر لأخذ كل ما في الصحن، إلى مطعم فاخر (GraphQL) حيث تعطي النادل قائمة دقيقة بما تريده، ويحضره لك بالضبط كما طلبت، في طبق واحد.

مع GraphQL، العميل هو من يقرر شكل البيانات التي يريدها. يرسل “استعلاماً” يصف البيانات المطلوبة بدقة، والسيرفر يرد باستجابة JSON تطابق تماماً شكل الاستعلام.

كيف يعمل GraphQL؟ (مع أمثلة كود)

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

حل مشكلة الـ Over-fetching

بدلاً من طلب GET /api/users/me، سنرسل طلب POST إلى نقطة النهاية الوحيدة لـ GraphQL (عادة /graphql) مع الاستعلام التالي:


query GetMyProfileInfo {
  me {
    firstName
    profilePicture
  }
}

والسيرفر سيرد بـ:


{
  "data": {
    "me": {
      "firstName": "عمر",
      "profilePicture": "https://.../image.png"
    }
  }
}

لاحظ؟ حصلنا على ما نحتاجه بالضبط، لا أكثر ولا أقل. لا هدر في البيانات، وسرعة فائقة. طلبنا الملح، فأعطانا الملح فقط. ✨

حل مشكلة الـ Under-fetching (N+1)

الآن، لنحل مشكلة المقالات والكتّاب. بدلاً من 21 رحلة، سنقوم برحلة واحدة فقط باستعلام يطلب كل شيء نريده دفعة واحدة:


query GetPostsWithAuthors {
  posts {
    id
    title
    author {
      name
    }
  }
}

والاستجابة السحرية ستكون:


{
  "data": {
    "posts": [
      {
        "id": 1,
        "title": "مقدمة في GraphQL",
        "author": {
          "name": "أبو عمر"
        }
      },
      {
        "id": 2,
        "title": "أداء الواجهات البرمجية",
        "author": {
          "name": "علي"
        }
      },
      {
        "id": 3,
        "title": "REST مقابل GraphQL",
        "author": {
          "name": "أبو عمر"
        }
      }
    ]
  }
}

في رحلة واحدة، حصلنا على قائمة المقالات ومعها اسم كاتب كل مقال. لقد قضينا على مشكلة N+1 من جذورها.

المخطط (Schema) والأنواع (Types): العقد اللي بينك وبين السيرفر

قد تتساءل: “كيف يعرف السيرفر كيفية ربط المقالات بالكتّاب؟”. الجواب يكمن في “المخطط” أو الـ Schema. المخطط هو العقد المكتوب بين العميل والسيرفر. هو يصف كل البيانات المتاحة في الواجهة البرمجية وكيفية الوصول إليها.

مثال بسيط على مخطط لمثالنا السابق باستخدام لغة تعريف المخطط (SDL):


# يصف أنواع البيانات المتاحة

type Post {
  id: ID!
  title: String!
  content: String
  author: User! # كل مقال له كاتب من نوع User
}

type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!] # كل مستخدم يمكن أن يكون لديه قائمة من المقالات
}

# يصف الاستعلامات الممكنة

type Query {
  posts: [Post!]
  post(id: ID!): Post
  users: [User!]
  user(id: ID!): User
  me: User
}

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

نصائح من خبرة أبو عمر: متى تستخدم GraphQL؟

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

استخدم GraphQL في الحالات التالية:

  • تطبيقات الموبايل: حيث تكون الشبكة غير مستقرة واستهلاك البيانات حساساً.
  • تطبيقات الصفحة الواحدة (SPAs): مثل React و Vue و Angular، التي تحتوي على واجهات مستخدم معقدة تحتاج بيانات من مصادر متعددة.
  • عندما يكون لديك عدة عملاء: مثل تطبيق ويب، تطبيق موبايل (iOS/Android)، وساعة ذكية. GraphQL يسمح لكل عميل بطلب البيانات التي يحتاجها فقط.
  • عندما تريد تمكين فرق الواجهة الأمامية: GraphQL يمنح مطوري الـ Frontend استقلالية أكبر لتجربة وتطوير الميزات دون الحاجة لطلب تغييرات مستمرة من فريق الـ Backend.

قد يكون REST خياراً أفضل في الحالات التالية:

  • الواجهات البرمجية البسيطة جداً: إذا كان لديك تطبيق CRUD بسيط، فإن تعقيد إعداد GraphQL قد لا يكون مبرراً.
  • الواجهات التي تركز على الملفات والموارد الثابتة: REST ممتازة في التعامل مع الموارد مثل الصور والفيديوهات، والاستفادة من آليات التخزين المؤقت (Caching) على مستوى الـ HTTP.
  • عندما يكون فريقك غير مستعد: يتطلب GraphQL منحنى تعلم. إذا كان فريقك مرتاحاً جداً مع REST والوقت ضيق، قد لا يكون الانتقال هو الخيار الأمثل الآن.

الخلاصة: اختر الأداة المناسبة للمهمة

في النهاية، قصة GraphQL و REST ليست قصة صراع بين الخير والشر. هي قصة تطور. REST وضعت الأساس، و GraphQL جاء ليبني عليه ويحل مشاكل ظهرت مع تطور تطبيقاتنا.

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

نصيحتي الأخيرة لك: لا تخف من التجربة. إذا كنت تعاني من نفس المشاكل التي وصفناها، ابدأ صغيراً. جرّب بناء واجهة GraphQL بسيطة لمشروع جانبي، أو حتى قم بإنشاء “طبقة” GraphQL فوق واجهة REST موجودة لديك باستخدام أدوات مثل Apollo Server.

شاهد الفرق بنفسك، وبعدها قرر. يلا يا جماعة، شدّوا الهمة، عالم البرمجة واسع ويحتاج منا التعلم المستمر. بالتوفيق! 💪

أبو عمر

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

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

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

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

آخر المدونات

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

تحديثات قاعدة البيانات بدون توقف: كيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من جحيم التوقفات المجدولة؟

هل سئمت من إيقاف الخدمة مع كل تحديث لهيكلة قاعدة البيانات؟ أشارككم قصة حقيقية وكيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من ليالي النشر الطويلة والمُجهدة،...

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

كانت إعادة المحاولة كارثة: كيف أنقذتنا مفاتيح عدم تكرار العمليات (Idempotency Keys) من جحيم الفواتير المزدوجة؟

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

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

من التوقف التام إلى النجاة: كيف أنقذتنا استراتيجية “الضوء المرشد” (Pilot Light) يوم انقطعت السحابة؟

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

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

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

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

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

من الانتظار لأيام إلى الدفع في ثوانٍ: كيف أنقذتنا شبكات الدفع الفوري من جحيم التحويلات البنكية؟

أسرد لكم من واقع تجربتي كـ "أبو عمر"، كيف عانينا من بطء وتكلفة التحويلات البنكية الدولية، وكيف جاءت شبكات الدفع الفوري ومعيار ISO 20022 لتكون...

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

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

في هذه المقالة، أشارككم قصة حقيقية من قلب المعركة التقنية مع "خوادم ندفات الثلج" الفوضوية. سنغوص في مفهوم "الكود كبنية تحتية" (IaC) وكيف أن أدوات...

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

كانت تغطية الاختبارات 100% لكن الأخطاء تتسرب: كيف أنقذنا “الاختبار الطفري” من جحيم الثقة الزائفة؟

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

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