يا جماعة الخير، السلام عليكم ورحمة الله. اسمي أبو عمر، مبرمج فلسطيني قضيت سنين طويلة من عمري بين الأكواد والخوارزميات. اليوم بدي أحكيلكم قصة صارت معي قبل كم سنة، قصة كانت راح تودي مشروع كامل في داهية، لولا رحمة ربنا وتقنية غيرت نظرتي لبناء الواجهات البرمجية (APIs) تمامًا.
كنا شغالين على تطبيق موبايل كبير لعميل مهم، تطبيق فيه شاشات كثيرة ومعقدة: صفحة رئيسية فيها آخر الأخبار ومنتجات مقترحة وأصدقاء متصلين، وصفحة للمستخدم فيها معلوماته ومنشوراته وصوره، وصفحة للمنتج فيها تفاصيله وتقييماته وتعليقات المشترين… يعني “كوكتيل” بيانات من كل شكل ولون. الواجهة الخلفية (Backend) كانت مبنية باستخدام REST APIs التقليدية، وهذا كان الوضع الطبيعي وقتها.
في البداية، الأمور كانت تمام. لكن مع زيادة البيانات وتعقيد الشاشات، بدأ التطبيق يصير بطيء… بطيء بشكل لا يطاق. فريق الموبايل كل يوم يشتكي: “يا أبو عمر، شاشة البروفايل بتحمّل 10 ثواني!”، “يا زلمة، الصفحة الرئيسية بتعمل 7 طلبات API مختلفة عشان تكتمل!”. كنت أفتح أدوات المطورين وأشوف الشبكة (Network Tab) عبارة عن شلال من الطلبات، نازلة ورا بعضها زي المطر. حسينا حالنا بنغرق حرفيًا، والعميل بدأ صبره ينفد. هون أدركت إن المشكلة أعمق من مجرد “بطء”، المشكلة كانت في صميم تصميم الـ API تبعنا. كنا ضايعين بين مطرقة الـ Over-fetching وسندان الـ Under-fetching.
ما هو الجحيم المزدوج: الـ Over-fetching والـ Under-fetching؟
قبل ما أكمل القصة، خلوني أشرحلكم هدول المصطلحين اللي كانوا سبب مصيبتنا، لأنه فهمهم هو مفتاح الحل.
المشكلة الأولى: الإفراط في جلب البيانات (Over-fetching)
بكل بساطة، الـ Over-fetching بيصير لما الـ API يرجعلك بيانات أكثر بكثير من اللي بتحتاجه في شاشة معينة. تخيل معي عندك شاشة في التطبيق بتعرض بس اسم المستخدم وصورته الشخصية. لكن الـ API اللي صممناه كان برجع كل معلومات المستخدم في طلب واحد.
يعني كنا نعمل طلب GET على /api/users/123، والرد اللي يجينا يكون هيك:
{
"id": "123",
"name": "أبو عمر",
"username": "abu_omar_dev",
"email": "abu.omar@example.com",
"profile_picture_url": "https://example.com/pic.jpg",
"bio": "مبرمج فلسطيني يحب القهوة والكود النظيف...",
"date_of_birth": "1985-01-15",
"address": {
"street": "شارع يافا",
"city": "القدس",
"country": "فلسطين"
},
"followers_count": 10000,
"following_count": 500,
"last_login": "2023-10-26T10:00:00Z"
// ... وغيرها من البيانات الكثيرة
}
إحنا كل اللي محتاجينه هما حقلين: name و profile_picture_url. لكننا استقبلنا كل هاي البيانات اللي ما الها لزوم في هاي الشاشة بالذات. هاي البيانات الزائدة بتستهلك انترنت (باندويث)، بتزيد من وقت التحميل، وبتخلي التطبيق أبطأ، خصوصًا على شبكات الموبايل الضعيفة. هاي هي ورطة الـ Over-fetching.
المشكلة الثانية: القصور في جلب البيانات (Under-fetching)
هاي المشكلة هي الوجه الآخر للعملة، وهي تمامًا عكس اللي قبلها. الـ Under-fetching بيصير لما الـ API ما يرجعلك كل البيانات اللي بتحتاجها في طلب واحد، فتضطر تعمل طلبات إضافية عشان تكمل الصورة.
لنرجع لمثالنا، تخيل عندنا شاشة بتعرض تفاصيل مقال (Post)، اسم كاتب المقال (Author)، وقائمة بالتعليقات (Comments) على المقال. باستخدام REST API تقليدي، السيناريو كان كالتالي:
- الطلب الأول: جلب بيانات المقال نفسه من
/api/posts/45. - الرد يحتوي على
authorIdلكن ليس اسم الكاتب. - الطلب الثاني: جلب بيانات الكاتب باستخدام الـ ID اللي حصلنا عليه:
/api/users/123. - الطلب الثالث: جلب التعليقات المتعلقة بالمقال:
/api/posts/45/comments.
شايفين الكارثة؟ شاشة واحدة بسيطة احتاجت 3 طلبات متتالية للشبكة! هذا ما يسمى بـ “شلال الطلبات” (Request Waterfall)، وهو قاتل لأداء التطبيق. كل طلب فيه وقت ضايع في الذهاب والإياب للسيرفر. هاي هي ورطة الـ Under-fetching.
اللقاء مع المنقذ: GraphQL
في عز حيرتي ويأسي وأنا ببحث عن حلول، قرأت مقالة عن تقنية اسمها GraphQL كانت فيسبوك طورتها داخليًا لحل نفس هاي المشاكل في تطبيق الموبايل تبعهم. في البداية كنت متشكك، “كمان تقنية جديدة نتعلمها؟ مش ناقصنا وجع راس!”. لكن كل ما قرأت عنها أكثر، كل ما حسيت إنها ممكن تكون الحل اللي بندور عليه.
ما هو GraphQL باختصار؟
GraphQL مش لغة برمجة، ولا قاعدة بيانات، ولا مكتبة محددة. هي عبارة عن “لغة استعلام” (Query Language) للـ API تبعك، ومواصفة (Specification) لكيفية عمل السيرفر عشان يفهم هاي اللغة.
الفكرة الجوهرية في GraphQL بسيطة وعبقرية: بدلاً من وجود عشرات نقاط النهاية (Endpoints) الثابتة في السيرفر (زي REST)، بيكون عندك نقطة نهاية واحدة فقط. العميل (التطبيق) هو اللي بقرر شو البيانات اللي بده إياها بالضبط، عن طريق إرسال “استعلام” (Query) يوصف شكل البيانات المطلوبة. السيرفر بيفهم هذا الاستعلام، وبيرجعلك JSON بنفس الشكل اللي طلبته بالضبط. لا زيادة ولا نقصان.
كيف أنقذنا GraphQL من الجحيم؟
بعد ما اقتنعت بالفكرة، عملت اجتماع مع الفريق وعرضت عليهم GraphQL. كان في مقاومة في البداية، لكن لما عملنا بروتوتايب بسيط وشافوا الفرق بأعينهم، الكل تحمس. وبدأنا رحلة الإنقاذ.
القضاء على الـ Over-fetching: اطلب ما تحتاجه فقط
تذكروا مثال صفحة المستخدم اللي كنا بنحتاج فيها بس الاسم والصورة؟ مع GraphQL، صار بإمكان تطبيق الموبايل يرسل هذا الاستعلام البسيط:
query GetUserHeaderData {
user(id: "123") {
name
profile_picture_url
}
}
والرد اللي كان يجي من السيرفر كان تحفة فنية من البساطة والكفاءة:
{
"data": {
"user": {
"name": "أبو عمر",
"profile_picture_url": "https://example.com/pic.jpg"
}
}
}
فقط البيانات المطلوبة! لا بايت واحد زيادة. هذا لوحده سرّع تحميل الشاشة بشكل ملحوظ جدًا.
القضاء على الـ Under-fetching: اجمع كل شيء في طلب واحد
أما بالنسبة لمشكلة شاشة المقال اللي كانت تحتاج 3 طلبات، فكان الحل مع GraphQL ساحرًا. صار بإمكاننا نكتب استعلام واحد يطلب كل شيء دفعة واحدة:
query GetPostPageData {
post(id: "45") {
title
content
author {
name
username
}
comments {
text
createdAt
author {
name
}
}
}
}
شايفين الجمال؟ في طلب واحد فقط، طلبنا المقال، وداخله طلبنا معلومات الكاتب، وداخله طلبنا قائمة التعليقات ومعلومات كاتب كل تعليق. السيرفر بيفهم هاي العلاقات وبيرجع كل هاي البيانات في رد واحد منظم. شلال الطلبات اختفى، وتحول إلى طلب واحد قوي وسريع.
نصائح من مطبخ أبو عمر
بعد ما خضت هاي التجربة وغيرها، تعلمت كم شغلة مهمة عن GraphQL بحب أشاركم إياها.
1. GraphQL ليس بديلاً كاملاً لـ REST
لا تفكر إنه لازم ترمي كل شغل الـ REST اللي عندك وتبدأ من الصفر. REST لا يزال ممتازًا ومناسبًا جدًا للـ APIs البسيطة والموجهة للموارد (Resource-oriented). GraphQL يلمع ويتألق لما يكون عندك تطبيقات عميلة متعددة (ويب، موبايل، ساعة ذكية) باحتياجات بيانات مختلفة، أو لما تكون علاقات البيانات عندك معقدة ومتشعبة.
2. ابدأ صغيرًا وقم بالتغليف (Wrap, don’t rewrite)
إذا عندك نظام قائم على REST API، أصعب وأخطر إشي ممكن تعمله هو إعادة كتابة كل شيء من الصفر. النصيحة الذهبية: ابدأ ببناء “طبقة GraphQL” فوق الـ REST APIs الموجودة عندك. يعني سيرفر GraphQL تبعك، بدل ما يكلم قاعدة البيانات مباشرة، بيقوم هو باستدعاء الـ REST APIs القديمة ويجمع البيانات منها ويرجعها للعميل. هذه طريقة آمنة وتدريجية للانتقال.
3. فكر في التخزين المؤقت (Caching) بذكاء
واحدة من نقاط قوة REST هي سهولة التخزين المؤقت على مستوى HTTP، لأن كل URL يمثل موردًا محددًا. مع GraphQL، كل الطلبات بتروح لنفس الـ URL (مثلاً /graphql)، وهذا يجعل التخزين المؤقت على مستوى HTTP أصعب. الحل يكمن في مكتبات العميل (Client Libraries) القوية مثل Apollo Client أو Relay. هذه المكتبات تقوم بعمل “تخزين مؤقت مُطَبَّع” (Normalized Caching) ذكي جدًا، بحيث تفهم البيانات اللي رجعت وتخزنها بشكل منظم، وهذا يقلل الطلبات للسيرفر بشكل كبير.
4. الأمان أولاً يا صاحبي
مع القوة تأتي المسؤولية. GraphQL بيعطي العميل قوة كبيرة، وهذا ممكن يُستغل بشكل سيء. ممكن عميل خبيث يرسل استعلامًا معقدًا جدًا ومتشعبًا لدرجة إنه يستهلك كل موارد السيرفر ويوقعه (Denial of Service). لازم تحمي سيرفرك عن طريق:
- تحديد عمق الاستعلام (Query Depth Limiting): منع الاستعلامات المتشعبة لأكثر من مستوى معين.
- تحليل تعقيد الاستعلام (Query Complexity Analysis): إعطاء “تكلفة” لكل حقل في الاستعلام، ورفض الاستعلامات اللي بتتجاوز تكلفة معينة.
- وضع مهلة زمنية (Timeout): إيقاف أي استعلام يأخذ وقتًا أطول من اللازم.
الخلاصة: هل يستحق GraphQL كل هذا العناء؟
بالنسبة لي ولفريقي، الجواب كان نعم وبقوة. GraphQL لم يحل مشاكل الأداء فقط، بل حسّن تجربة المطورين بشكل لا يصدق. صار فريق الواجهات الأمامية (Frontend) وفريق الموبايل عندهم استقلالية أكبر، وما عادوا بحاجة يطلبوا تعديلات على الـ API مع كل تغيير بسيط في تصميم الشاشة.
GraphQL ليس حلاً سحريًا لكل المشاكل، وهو يتطلب تعلمًا وتغييرًا في طريقة التفكير. لكن في عالم اليوم، حيث تتعدد الواجهات وتزداد تعقيدًا، فإن المرونة والقوة التي يقدمها تجعله أداة لا غنى عنها في صندوق أدوات أي مطور عصري.
في المرة القادمة التي تشعر فيها أن واجهاتك تغرق في بحر من الطلبات البطيئة، تذكر قصة أبو عمر. ربما حان الوقت لتعطي GraphQL فرصة، فقد يكون هو طوق النجاة الذي يحتاجه مشروعك. بالتوفيق يا جماعة! 💪