كنت أغرق في بحر من الـ Endpoints: كيف أنقذني GraphQL من فوضى واجهات REST؟

مقدمة: قصة فنجان قهوة وكابوس الـ Endpoints

يا جماعة، السلام عليكم. معكم أخوكم أبو عمر.

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

كنت أقضي صباحي مع فنجان القهوة وأنا أكتب الـ endpoints الجديدة. /users عشان تجيب المستخدمين، /posts عشان المنشورات، /users/:id/posts عشان تجيب منشورات مستخدم معين. شعور بالإنجاز، صح؟

لكن مع الوقت، كبر المشروع، وصار معنا فريق لتطبيق الموبايل (iOS و Android) وفريق للويب. وهنا بلش وجع الراس. فريق الويب بده يعرض اسم المستخدم وصورته وآخر 3 منشورات إله في الصفحة الرئيسية. فريق الموبايل بده يعرض بس اسم المستخدم وصورته في قائمة الأصدقاء. فريق تاني بده إحصائيات عن عدد الإعجابات والتعليقات لكل منشور.

فجأة، لقيت حالي بغرق. كل يوم طلبات جديدة: “أبو عمر، ممكن تعمللنا endpoint جديدة بس بترجع عدد التعليقات؟ الـ endpoint الحالية بترجع كل التعليقات وهذا بطيء عالموبايل”. “أبو عمر، بدنا نعدل الـ endpoint تبعت البروفايل عشان تضيف تاريخ الميلاد، بس ما بدنا إياها تظهر في نسخة الويب”.

صرت أعمل endpoints مخصصة لكل شاشة ولكل جهاز: /users/:id/summary، /posts/:id/stats، /mobile/v2/feed. الكود صار عبارة عن متاهة من الـ endpoints المتشابهة والمكررة، وكل تعديل صغير في مكان كان يتطلب تعديلات في 5 أماكن ثانية. حسيت إني مش مبرمج، حسيت إني “ترزي” بقعد أفصّل endpoints على مقاس كل واحد. وفي ليلة من الليالي، وأنا براجع كمية الـ endpoints اللي عملتها، قلت لحالي: “لهون وبس، لازم في حل أحسن”. وهون كانت بداية رحلتي مع GraphQL.

ما هي مشكلة REST API بالضبط؟

قبل ما نحكي عن المنقذ GraphQL، خلينا نفهم أصل المشكلة مع REST. معمارية REST عظيمة، وبسيطة، وخدمتنا لسنوات. فكرتها قائمة على “المصادر” (Resources). كل إشي هو مصدر، ولكل مصدر عنوان (URL) خاص فيه. لكن هاي البساطة هي نفسها اللي بتخلق مشاكل في التطبيقات المعقدة.

المشكلة الأولى: الجلب الزائد للبيانات (Over-fetching)

لما تطلب بيانات مستخدم من خلال endpoint مثل GET /api/users/1، السيرفر بقرر شو البيانات اللي رح يرجعها. غالبًا، رح يرجعلك كل معلومات المستخدم الموجودة في قاعدة البيانات.


// Request
GET /api/users/1

// Response
{
  "id": 1,
  "name": "أبو عمر",
  "email": "abu.omar@example.com",
  "birthdate": "1985-01-15T00:00:00.000Z",
  "address": "القدس، فلسطين",
  "createdAt": "2020-05-10T12:00:00.000Z",
  "bio": "مبرمج يحب القهوة والكود النظيف."
}

لكن ماذا لو كانت واجهة المستخدم تحتاج فقط لعرض اسم المستخدم (name)؟ أنت هيك استهلكت بيانات (bandwidth) وحملت بيانات ما إلها لزوم (birthdate, address, …إلخ). تخيل هذا على تطبيق موبايل بيستخدم باقة الإنترنت المحدودة، مشكلة!

المشكلة الثانية: الجلب الناقص للبيانات (Under-fetching)

هاي المشكلة هي الوجه الآخر للعملة. لنفرض إنك بدك تعرض صفحة بروفايل المستخدم مع آخر 5 منشورات إله. باستخدام REST، أنت مضطر تعمل طلبين (أو أكثر) للسيرفر:

  1. الطلب الأول: جلب معلومات المستخدم.

    GET /api/users/1
  2. الطلب الثاني: جلب منشورات هذا المستخدم.

    GET /api/users/1/posts?limit=5

هذا يعني رحلة ذهاب وإياب مرتين بين العميل (Client) والخادم (Server)، مما يزيد من وقت التحميل ويؤثر على تجربة المستخدم. هذه المشكلة تُعرف أحيانًا بمشكلة “N+1 requests”.

GraphQL: طوق النجاة الذي كنت أبحث عنه

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

الفكرة عبقرية وبسيطة: بدل ما يكون عندك عشرات الـ endpoints اللي الخادم بحدد شكل استجابتها، بكون عندك endpoint واحدة فقط (عادة /graphql). العميل (Client) هو اللي بقرر شكل البيانات اللي بده إياها بالضبط، وبيرسل استعلام (Query) يوصف هاي البيانات.

نصيحة من أبو عمر: فكر في REST كأنك بتطلب وجبة من مطعم بقائمة ثابتة (Set Menu). رح تجيك الوجبة كاملة مع المقبلات والطبق الجانبي، حتى لو ما بدك إياهم. أما GraphQL، فهو أشبه بالبوفيه المفتوح، بتروح وبتعبي صحنك بس بالأشياء اللي بتحبها وبالكمية اللي بدك إياها.

كيف تعمل GraphQL؟ مثال عملي

لنفترض نفس السيناريو السابق: بدنا نعرض اسم المستخدم ومنشوراته الخمسة الأخيرة. مع GraphQL، العميل بيرسل طلب POST واحد للـ endpoint الوحيدة /graphql، وفي جسم الطلب (body) بيحط الاستعلام التالي:


query GetUserWithPosts {
  user(id: 1) {
    name
    posts(limit: 5) {
      title
      createdAt
    }
  }
}

والسيرفر رح يرجع استجابة JSON بنفس شكل الاستعلام بالضبط، لا زيادة ولا نقصان:


{
  "data": {
    "user": {
      "name": "أبو عمر",
      "posts": [
        {
          "title": "مقدمة إلى GraphQL",
          "createdAt": "2023-10-27T10:00:00.000Z"
        },
        {
          "title": "لماذا أحب Vim",
          "createdAt": "2023-10-25T14:30:00.000Z"
        },
        // ... 3 more posts
      ]
    }
  }
}

شايفين الجمال؟ طلب واحد، جاب كل البيانات اللي بدنا إياها بالضبط. لا over-fetching ولا under-fetching. الواجهة الأمامية (Frontend) صارت هي المتحكمة، وفريق الواجهة الخلفية (Backend) مثلي، صار يركز على توصيف البيانات المتاحة (Schema) وتوفيرها، بدل ما يركز على بناء endpoints مخصصة لكل شاشة.

المكونات الأساسية لـ GraphQL

عشان تفهم السحر اللي بصير، لازم تعرف 3 مفاهيم أساسية:

1. المخطط (Schema)

الـ Schema هو قلب أي GraphQL API. هو العقد بين العميل والخادم. أنت بتوصف فيه كل أنواع البيانات اللي ممكن العميل يطلبها، وكيفية ارتباطها ببعضها. هذا المخطط مكتوب بلغة اسمها Schema Definition Language (SDL).

مثال على مخطط بسيط للمستخدم والمنشور:


# يمثل مستخدم في النظام
type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]
}

# يمثل منشور كتبه مستخدم
type Post {
  id: ID!
  title: String!
  content: String
  author: User!
  createdAt: String
}

# النقطة اللي بتبدأ منها كل الاستعلامات
type Query {
  user(id: ID!): User
  posts: [Post!]
}

الـ Schema موثق ذاتيًا (self-documenting). أي مطور جديد بيقدر يقرأه ويفهم كل إمكانيات الـ API فورًا. هذا إشي مرتب جدًا!

2. الاستعلامات (Queries)

كما رأينا في المثال السابق، الـ Queries هي الطريقة اللي العميل بطلب فيها البيانات للقراءة. مرونتها عالية جدًا، والعميل بحدد الحقول اللي بده إياها بالضبط.

3. التعديلات (Mutations)

طيب، كيف بنعدل البيانات (إنشاء، تحديث، حذف)؟ هنا يأتي دور الـ Mutations. هي تشبه الـ Queries في تركيبتها، لكنها مخصصة لعمليات الكتابة. بالعادة، بنحطها تحت نوع خاص اسمه Mutation في الـ Schema.


type Mutation {
  createPost(title: String!, content: String): Post
  updateUser(id: ID!, name: String): User
}

ولاستخدامها، يرسل العميل طلب Mutation كالتالي:


mutation CreateNewPost {
  createPost(title: "مقالتي الجديدة", content: "محتوى المقالة...") {
    id
    title
  }
}

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

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

بعد ما اشتغلت على GraphQL في عدة مشاريع، جمعتلكم شوية نصائح من القلب:

  • GraphQL ليست الحل لكل المشاكل: إذا كان مشروعك بسيطًا (مدونة شخصية، صفحة هبوط)، فغالبًا REST API ستكون أسهل وأسرع في التنفيذ. GraphQL تظهر قوتها الحقيقية في التطبيقات الكبيرة والمعقدة ذات العلاقات المتشعبة بين البيانات والعملاء المتعددين (ويب، موبايل، …إلخ).
  • ابدأ بالتدريج: مش ضروري تهدم كل الـ REST API اللي عندك. يمكنك بناء طبقة GraphQL فوق واجهات REST الحالية. هذا يسمى “GraphQL Wrapper”. هكذا، يستفيد فريق الـ Frontend من مزايا GraphQL فورًا، بينما تقوم أنت بترحيل المنطق تدريجيًا في الخلفية.
  • استخدم الأدوات المناسبة: عالم GraphQL مليء بالأدوات الرائعة. في جهة الخادم، مكتبات مثل Apollo Server (لـ Node.js) تسهل عليك بناء الخادم. وفي جهة العميل، مكتبات مثل Apollo Client و Relay توفر ميزات مذهلة مثل إدارة الحالة (State Management) والتخزين المؤقت (Caching).
  • انتبه للأمان والأداء: القوة الكبيرة تأتي مع مسؤولية كبيرة. بما أن العميل يستطيع طلب ما يشاء، قد يرسل استعلامًا معقدًا جدًا يستهلك موارد الخادم (مثل طلب مستخدم، ثم كل أصدقائه، ثم كل أصدقاء أصدقائه!). لحل هذا، يجب استخدام تقنيات مثل تحديد عمق الاستعلام (Query Depth Limiting)، وتحديد تعقيد الاستعلام (Query Complexity Analysis)، وبالطبع، التأكد من صلاحيات المستخدم (Authorization) داخل الـ Resolvers.

الخلاصة: هل يجب أن تتخلى عن REST؟

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

هل يجب عليك تعلمها؟ بالتأكيد. هل يجب أن تستخدمها في كل مشروع؟ لا. افهم المشكلة التي تحاول حلها أولاً، ثم اختر الأداة المناسبة. لكن في المرة القادمة التي تجد فيها نفسك تغرق في بحر من الـ Endpoints، تذكر أن هناك طوق نجاة اسمه GraphQL قد يكون هو الحل الذي تبحث عنه. 🚀

وما تخافوا تجربوا إشي جديد، أفضل استثمار هو الاستثمار في نفسك وفي معرفتك. بالتوفيق يا أبطال! 🙏

أبو عمر

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

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

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

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

آخر المدونات

أتمتة العمليات

كانت عملياتنا كالدومينو: كيف أنقذنا “منسق سير العمل” من جحيم الفشل المتتالي؟

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

13 مايو، 2026 قراءة المزيد
نصائح برمجية

كانت شفرتنا هرماً من الجحيم: كيف أنقذتنا ‘شروط الحماية’ (Guard Clauses) من فوضى الـ if-else المتداخلة؟

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

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

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

في هذه المقالة، أسرد لكم تجربتي كـ"أبو عمر" مع جحيم الأنظمة المترابطة بإحكام (Tight Coupling) وكيف كانت "المعمارية القائمة على الأحداث" (Event-Driven Architecture) طوق النجاة...

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

متجر الميزات (Feature Store): كيف أنقذنا مشروعنا من جحيم “الانحراف التدريبي-التنبؤي”؟

أشارككم قصة حقيقية عن "الانحراف التدريبي-التنبؤي" (Training-Serving Skew)، الكابوس الصامت الذي كاد أن يدمر أحد مشاريعنا في الذكاء الاصطناعي. اكتشفوا كيف كان "متجر الميزات" (Feature...

13 مايو، 2026 قراءة المزيد
خوارزميات

كانت كل عملية فحص تضرب قاعدة البيانات: كيف أنقذنا ‘مرشح بلوم’ (Bloom Filter) من جحيم الاستعلامات غير الضرورية؟

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

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

حملاتنا الإعلانية كانت عمياء: كيف أنقذتنا واجهة برمجة تطبيقات التحويلات (CAPI) من جحيم البيانات المفقودة؟

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

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

كان كل زر في تطبيقنا قصة مختلفة: كيف أنقذنا ‘نظام التصميم’ (Design System) من جحيم الفوضى البصرية؟

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

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

كانت خدماتنا المصغرة مكشوفة وفوضوية: كيف أنقذتنا ‘بوابة الـ API’ من جحيم الأمان والمراقبة؟

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

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