السلام عليكم يا جماعة الخير، معكم أخوكم أبو عمر.
قبل كم سنة، كنت شغال على مشروع لتطبيق جوال، تطبيق اجتماعي بسيط لمشاركة الصور واليوميات. كنت متحمس جداً للفكرة، والفريق كله كان شغال ليل نهار. الواجهات الأمامية كانت جاهزة وشكلها “بياخذ العقل”، والواجهات الخلفية (Backend) كانت مبنية باستخدام معمارية REST API المعتادة. الأمور كانت تبدو تمام التمام على حاسوبي ومع شبكة الواي فاي السريعة في المكتب.
لكن المصيبة بانت لما بلشنا نجرّب التطبيق على شبكات الجوال الحقيقية (3G وقتها). فتح الصفحة الرئيسية كان يأخذ ثوانٍ طويلة، والتنقل بين الصفحات كان كابوساً. المستخدمون التجريبيون اشتكوا من بطء التطبيق واستهلاكه الكبير لباقة الإنترنت. شعرت بإحباط شديد، كيف لتطبيق بسيط أن يكون بهذا البطء؟
جلست مع نفسي، “فتحت فنجان قهوة سادة”، وبدأت أحلل طلبات الشبكة (Network Requests) اللي ببعتها التطبيق. وهنا كانت الصدمة. لعرض قائمة بسيطة من المنشورات، واللي كل اللي بحتاجه فيها هو صورة المستخدم واسمه وأول سطر من المنشور، كانت الـ API بترجعلي كل إشي! كلللل إشي حرفياً: بيانات المستخدم الكاملة، كل التعليقات على المنشور، قائمة الناس اللي عملت “لايك”، تاريخ التعديل، والمزيد من البيانات اللي ما إلها أي لزمة في هاي الشاشة بالذات. كانت الواجهة الأمامية تغرق في بحر من البيانات غير الضرورية. هذا هو ما نسميه يا إخواني بـ “جحيم الـ Over-fetching”.
هذه القصة كانت نقطة تحول في مسيرتي المهنية، واليوم سأشارككم كيف أنقذتني تقنية GraphQL من هذا الجحيم.
ما هو الـ Over-fetching؟ تشريح المشكلة
ببساطة شديدة، الـ Over-fetching يعني “جلب بيانات أكثر من اللازم”. عندما يقوم تطبيق العميل (المتصفح أو تطبيق الجوال) بطلب بيانات من الخادم عبر واجهة برمجية (API)، ويقوم الخادم بإرسال حزمة بيانات أكبر بكثير مما يحتاجه العميل لعرض الواجهة الحالية.
دعونا نأخذ مثالاً أوضح. تخيل أن لديك مدونة، وتحتاج إلى عرض قائمة بآخر 10 مقالات في الصفحة الرئيسية. كل ما تحتاجه في هذه القائمة هو:
- عنوان المقال (title)
- اسم الكاتب (authorName)
- صورة المقال المصغرة (thumbnailUrl)
في عالم REST API التقليدي، من المحتمل أن يكون لديك نقطة نهاية (endpoint) مثل GET /api/posts. عند استدعاء هذه النقطة، قد يبدو الرد الذي يصلك هكذا لكل مقال في القائمة:
{
"id": 123,
"title": "عنوان المقال الأول",
"content": "محتوى المقال كاملاً... نص طويل جداً جداً...",
"author": {
"id": 45,
"name": "أبو عمر",
"bio": "مبرمج ومطور برمجيات فلسطيني...",
"social_links": {
"twitter": "...",
"linkedin": "..."
}
},
"tags": ["تطوير", "برمجة", "API"],
"comments": [
{ "user": "أحمد", "comment": "مقال رائع!" },
{ "user": "سارة", "comment": "شكراً على المعلومات" },
// ... وقد يكون هناك 100 تعليق آخر
],
"createdAt": "2023-10-27T10:00:00Z",
"updatedAt": "2023-10-27T12:30:00Z",
"thumbnailUrl": "url/to/image.jpg"
}
لاحظت المشكلة؟ أنت تحتاج فقط 3 حقول، ولكن الخادم أرسل لك 8 حقول رئيسية، بعضها يحتوي على كائنات متداخلة وقوائم طويلة (مثل التعليقات). الآن اضرب حجم هذه البيانات الزائدة في 10 مقالات. النتيجة هي طلب شبكة بطيء يستهلك بيانات ثمينة، خاصة على أجهزة الجوال.
الطريقة التقليدية (REST) ومحدوديتها
بالتأكيد، هناك حلول لهذه المشكلة في عالم REST. لكنها، من وجهة نظري، أشبه بوضع “لصقات جروح” على مشكلة نظامية.
الحل الأول: إنشاء نقاط نهاية متعددة
الحل الأكثر شيوعاً هو أن يقوم فريق الواجهات الخلفية بإنشاء نقطة نهاية مخصصة لكل حالة استخدام. في مثالنا، قد ينشئون:
/api/posts: لإرجاع البيانات الكاملة للمقال./api/posts/summary: لإرجاع ملخص بسيط للمقالات لعرضه في القائمة.
هذا الحل يعمل في البداية، ولكنه يتحول إلى كابوس مع نمو التطبيق. ستجد نفسك مع عشرات، بل مئات، من نقاط النهاية. ماذا لو أرادت واجهة أخرى عرض العنوان والكاتب فقط؟ هل ننشئ /api/posts/titles؟ هذا يجعل صيانة الـ API وإدارتها أمراً معقداً جداً.
الحل الثاني: استخدام معاملات الاستعلام (Query Parameters)
حل آخر هو السماح للعميل بتحديد الحقول التي يريدها عبر معاملات الاستعلام، هكذا:
GET /api/posts?fields=title,authorName,thumbnailUrl
هذا أفضل بكثير! لكنه ليس معياراً قياسياً في REST، ويتطلب تنفيذاً مخصصاً على الخادم لكل نقطة نهاية. كما أنه يصبح معقداً جداً عند التعامل مع البيانات المتداخلة (Nested Data). كيف ستطلب اسم الكاتب فقط دون بقية بياناته؟ ربما هكذا؟ fields=title,author.name,thumbnailUrl. الأمر يبدأ بالخروج عن السيطرة بسرعة.
المنقذ GraphQL: كيف غيرت قواعد اللعبة؟
هنا يأتي دور GraphQL، وهي ليست مكتبة أو إطار عمل، بل هي **لغة استعلام للـ APIs** (Query Language for APIs) تم تطويرها بواسطة فيسبوك. الفكرة عبقرية وبسيطة في آن واحد.
المبدأ بسيط: أنت تطلب، الخادم يجيب على قد الطلب
مع GraphQL، لم يعد الخادم هو من يقرر شكل البيانات المرسلة. العميل هو من يقرر. يقوم العميل بإرسال “استعلام” (Query) يصف بدقة البيانات التي يحتاجها، بما في ذلك العلاقات بين البيانات. الخادم يقرأ هذا الاستعلام، ويجمع البيانات المطلوبة فقط، ويرسلها في استجابة تعكس تماماً شكل الاستعلام.
لنعد لمثال المدونة. بدلاً من استدعاء /api/posts، سيرسل العميل طلباً (عادةً POST) إلى نقطة نهاية GraphQL واحدة (مثلاً /graphql) مع الاستعلام التالي:
query GetPostsSummary {
posts(limit: 10) {
title
author {
name
}
thumbnailUrl
}
}
وستكون الاستجابة من الخادم مطابقة تماماً لهذا الشكل، لا زيادة ولا نقصان:
{
"data": {
"posts": [
{
"title": "عنوان المقال الأول",
"author": {
"name": "أبو عمر"
},
"thumbnailUrl": "url/to/image.jpg"
},
// ... 9 مقالات أخرى بنفس البنية
]
}
}
لاحظ الجمال هنا! طلبنا فقط اسم الكاتب (author.name) وليس كل بياناته، وهذا بالضبط ما حصلنا عليه. لا يوجد أي Over-fetching. إذا احتاجت شاشة أخرى في التطبيق لعرض التعليقات، يمكنها ببساطة إضافة comments إلى الاستعلام.
نقاط قوة GraphQL من واقع تجربتي
منذ أن تبنيت GraphQL في مشاريعي، لمست فوائد عملية غيرت طريقة عملي وفريقي بالكامل.
1. نهاية الـ Over-fetching والـ Under-fetching
تحدثنا مطولاً عن الـ Over-fetching. لكن GraphQL تحل أيضاً مشكلة معاكسة اسمها Under-fetching. هذه المشكلة تحدث عندما لا توفر نقطة النهاية كل البيانات المطلوبة، مما يضطر العميل إلى إرسال طلبات متعددة للحصول على كل ما يحتاجه. مثلاً، طلب /posts ثم لكل مقال، طلب /authors/{authorId}. هذا يسبب ما يسمى “N+1 problem”.
مع GraphQL، يمكنك جلب كل البيانات المترابطة التي تحتاجها في طلب واحد فقط، مهما كانت معقدة.
2. نقطة نهاية واحدة (Single Endpoint)
وداعاً لغابة نقاط النهاية في REST! معظم تطبيقات GraphQL تستخدم نقطة نهاية واحدة فقط (مثل /graphql). هذا يبسط الأمور بشكل لا يصدق من جهة العميل. كل التغييرات تتم عبر تعديل الاستعلامات، وليس عبر البحث عن نقاط نهاية جديدة.
3. توثيق ذاتي (Self-documenting)
نصيحة من أبو عمر: من أكبر المشاكل التي واجهتها مع REST APIs هي أن التوثيق (Documentation) دائماً ما يكون قديماً أو غير مكتمل. كم مرة قرأت توثيقاً يقول شيئاً، والـ API الفعلية تفعل شيئاً آخر؟
GraphQL مبنية على نظام أنواع صارم (Strongly Typed Schema). هذا الـ Schema يصف كل البيانات المتاحة في الـ API وكيفية طلبها. والأجمل من ذلك، أن هذا الـ Schema يمكن استكشافه برمجياً. أدوات مثل GraphiQL أو GraphQL Playground توفر لك واجهة تفاعلية لاستكشاف الـ API بالكامل، وقراءة التوثيق، وتجربة الاستعلامات مباشرةً. صار التوثيق جزءاً من الكود، مش ملف PDF بننساه.
4. تطوير أسرع للواجهات الأمامية
هذه كانت أكبر فائدة لفريقي. لم يعد مطورو الواجهات الأمامية (Frontend) ينتظرون فريق الواجهات الخلفية (Backend) لإنشاء أو تعديل نقطة نهاية. إذا احتاجوا حقلاً جديداً في واجهة ما، كل ما عليهم فعله هو إضافته إلى استعلام GraphQL الخاص بهم. هذا يمنحهم استقلالية وسرعة هائلة في التطوير.
نصائح أبو عمر: متى تستخدم GraphQL؟ وهل هو بديل كامل لـ REST؟
GraphQL ليست “الحل السحري” لكل المشاكل، ومن المهم أن نعرف متى نستخدمها.
- استخدم GraphQL عندما:
- تطبيقاتك لديها عملاء متنوعون (ويب، جوال، ساعة ذكية) ولكل منهم احتياجات بيانات مختلفة.
- واجهاتك معقدة وتحتاج إلى بيانات من مصادر متعددة في شاشة واحدة (مثل لوحات التحكم).
- سرعة الشبكة واستهلاك البيانات عامل حاسم (خصوصاً في تطبيقات الجوال).
- تريد تمكين فرق الواجهات الأمامية ومنحهم استقلالية أكبر.
- قد يكون REST خياراً أفضل عندما:
- الـ API بسيطة جداً وتتعامل مع موارد محددة وغير متغيرة (مثل API داخلية بين خدمتين مصغرتين).
- الاعتماد على آليات التخزين المؤقت (Caching) الخاصة بـ HTTP (مثل GET requests) هو أولوية قصوى. GraphQL عادةً ما تستخدم طلبات POST، مما يجعل التخزين المؤقت على مستوى HTTP أكثر تعقيداً.
والنصيحة الأهم: GraphQL و REST يمكن أن يتعايشا بسلام! يمكنك بناء واجهة GraphQL (Gateway) تجلس أمام خدمات REST المصغرة (Microservices) الموجودة لديك. تقوم هذه الواجهة بتجميع البيانات من خدمات REST المختلفة وتقديمها للعميل عبر GraphQL. هذا نمط قوي جداً في الأنظمة الكبيرة.
الخلاصة: هل تستحق GraphQL كل هذا العناء؟
بالنسبة لي، ولكثير من المطورين حول العالم، الجواب هو نعم، وبقوة. العناء الأولي في تعلم GraphQL وإعداد الخادم يقابله توفير هائل في الوقت والجهد على المدى الطويل، بالإضافة إلى تحسين جذري في أداء التطبيقات وتجربة المطورين.
يا جماعة الخير، التكنولوجيا أداة. والمهم هو أن نعرف أي أداة نستخدم ومتى نستخدمها. بالنسبة لي، GraphQL كانت المطرقة المناسبة تماماً للمسمار الذي كان “معذبني” لسنوات طويلة وهو الـ Over-fetching. لقد حررتني من قيود الـ API التقليدية وفتحت أمامي آفاقاً جديدة لبناء تطبيقات أسرع وأكثر كفاءة. 🚀
أتمنى أن تكون هذه التجربة مفيدة لكم. وإذا كان لديكم أي سؤال، فأنا جاهز في التعليقات. الله يوفقكم جميعاً.