السلام عليكم يا جماعة الخير،
أنا أبو عمر، وبدي أحكيلكم قصة صارت معي ومع فريقي قبل كم سنة. كنا شغالين على تطبيق اجتماعي ضخم، وكان فيه صفحة بروفايل للمستخدم هي قلب التطبيق النابض. الصفحة هاي كان لازم تعرض اسم المستخدم وصورته، وآخر 10 منشورات إله، وأول 5 تعليقات على كل منشور، وقائمة الأصدقاء، وعدد المتابعين… قائمة طويلة عريضة.
في البداية، كالعادة، استخدمنا REST APIs. وهون بلشت المعاناة. الواجهة الأمامية (Frontend) كانت تصرخ! عشان يعرضوا صفحة بروفايل وحدة، كانوا مضطرين يبعتوا طلب (request) عشان يجيبوا معلومات المستخدم، وبعد ما يرجع الجواب، يبعتوا طلب ثاني لمنشوراته، وطلب ثالث لأصدقائه، وطلبات متتالية لكل منشور عشان يجيبوا تعليقاته… كنا حرفيًا نغرق في بحر من استدعاءات الـ API. الوضع كان أشبه بالواحد اللي بروح على مطعم وبطلب كل صحن لحال، وبستنى بين كل صحن والثاني عشر دقايق. الأداء كان في الحضيض، والتطبيق بطيء جدًا.
وقتها قعدنا وحكينا: “يا جماعة، شو القصة؟ لازم يكون في حل أحسن من هالحكي”. وبعد بحث وتجربة، لقينا طوق النجاة اللي أنقذنا من هذا الجحيم: GraphQL.
ما هو جحيم الـ Over-fetching والـ Under-fetching؟
قبل ما نحكي عن الحل، خلينا نفهم أصل المشكلة بالزبط. المشكلتين هدول هما من أشهر عيوب بنية REST API التقليدية لما نتعامل مع تطبيقات معقدة.
المشكلة الأولى: الجلب الزائد (Over-fetching)
ببساطة، الـ Over-fetching بيصير لما الـ API يرجعلك بيانات أكثر من اللي بتحتاجها. تخيل إنك بدك تعرض قائمة بأسماء المستخدمين وصورهم الشخصية بس. لكن نقطة النهاية (endpoint) اللي عندك، اللي هي /api/users، بترجعلك كل اشي عن كل مستخدم: الاسم، الصورة، تاريخ الميلاد، الإيميل، العنوان، آخر مرة كان متصل، وقائمة طويلة من البيانات اللي ما بتلزمك حاليًا.
النتيجة؟ أنت بتحمّل بيانات زيادة عن اللزوم عبر الشبكة، وهذا بيبطئ التطبيق وبيستهلك باقة الإنترنت عند المستخدم، خصوصًا على الموبايل. إشي مش عملي بالمرة.
المشكلة الثانية: الجلب الناقص (Under-fetching)
هاي المشكلة هي الوجه الآخر للعملة، وهي اللي صارت معنا في قصة البروفايل. الـ Under-fetching بيصير لما نقطة النهاية ما ترجعلك كل البيانات اللي بتحتاجها في طلب واحد، فبتضطر تعمل طلبات إضافية.
مثالنا هو الأوضح:
GET /users/1لجلب معلومات المستخدم.GET /users/1/postsلجلب منشورات المستخدم.GET /users/1/followersلجلب متابعي المستخدم.
هذا السيناريو، اللي بنسميه “N+1 problem”، كارثي على أداء التطبيق وبيخلق تعقيد كبير في كود الواجهة الأمامية اللي لازم ينسق بين كل هاي الطلبات.
GraphQL: المنقذ الذي يمنحك القوة
هنا يأتي دور GraphQL. GraphQL مش لغة برمجة ولا هي مكتبة، هي لغة استعلام (Query Language) للـ API تم تطويرها في فيسبوك عام 2012 ونُشرت كمشروع مفتوح المصدر في 2015. الفكرة العبقرية وراها بسيطة جدًا:
بدل ما يكون الخادم (Server) هو اللي يقرر شكل وحجم البيانات اللي برجعها، العميل (Client) هو اللي بطلب بالزبط شو بده، وبالشكل اللي بده ياه، والخادم برد عليه بنفس الشكل تمامًا.
تخيلها كأنك رايح على بوفيه مفتوح. مع REST، أنت بتطلب “الوجبة رقم 5” وبتاخد كل إشي فيها سواء عجبك أو لأ. مع GraphQL، أنت بتمر على البوفيه وبتعبي صحنك بالزبط بالأشياء اللي بتحبها وبالكميات اللي بدك إياها. إشي مرتب!
كيف تعمل GraphQL على أرض الواقع؟
على عكس REST اللي بيعتمد على نقاط نهاية (endpoints) متعددة، GraphQL عادةً بتستخدم نقطة نهاية واحدة (مثل /graphql). العميل بيبعت طلب من نوع POST لهاي النقطة، وجسم الطلب (body) بيحتوي على الاستعلام.
النظام كله بيعتمد على 3 مفاهيم أساسية:
1. المخطط (Schema)
هو العقد أو “المنيو” اللي بين الخادم والعميل. أنت كمطور خلفية (backend) بتعرف كل أنواع البيانات الممكنة (Types) والعمليات المتاحة عليها (Queries, Mutations). هذا المخطط هو مصدر الحقيقة الوحيد، وبيسمح للفرق الأمامية والخلفية يشتغلوا بشكل مستقل.
# مثال على Schema بسيط
type User {
id: ID!
name: String
email: String
posts: [Post]
}
type Post {
id: ID!
title: String
content: String
comments(limit: Int = 5): [Comment]
}
type Comment {
id: ID!
text: String
author: User
}
# العمليات المتاحة للقراءة
type Query {
user(id: ID!): User
}
2. الاستعلامات (Queries)
هذا هو الطلب اللي ببعته العميل. العميل بيكتب استعلام بيشبه شكل JSON، وبيحدد فيه الحقول اللي بده إياها بالضبط.
لحل مشكلة صفحة البروفايل اللي حكينا عنها، بدل ما نبعت 5 أو 6 طلبات REST، بنبعت استعلام GraphQL واحد كالتالي:
query GetUserProfile {
user(id: "1") {
id
name
profilePicture
posts(limit: 10) {
title
comments(limit: 5) {
text
author {
name
}
}
}
followers(limit: 20) {
name
profilePicture
}
}
}
والجميل في الموضوع؟ الجواب اللي بيرجع من الخادم بيكون بنفس شكل الاستعلام بالزبط! لا زيادة ولا نقصان.
3. المُحلّلات (Resolvers)
هي “العضلات” اللي بتشتغل في الخفاء. لكل حقل (field) في المخطط تبعك، بيكون في دالة (function) على الخادم اسمها “resolver”. هاي الدالة هي المسؤولة عن جلب البيانات لهذا الحقل تحديدًا. ممكن تجيبها من قاعدة بيانات، من API ثاني، من ملف… من أي مكان.
الخادم لما يستقبل استعلام GraphQL، بيمشي على حقوله واحد واحد وبينفذ الـ resolver الخاص فيه. هذا بيعطي مرونة وقوة هائلة للخادم.
نصائح عملية من خبرة أبو عمر
بعد سنوات من الشغل على GraphQL، تعلمت كم شغلة مهمة بحب أشاركها معكم:
- GraphQL ليست بديلاً كاملاً لـ REST: لا تفكر إنك لازم ترمي كل شغل REST اللي عندك. REST ما زالت ممتازة للتطبيقات البسيطة، أو لما تكون طبيعة البيانات ثابتة جدًا، أو في عمليات مثل رفع الملفات. فكر في GraphQL كأداة قوية في صندوق أدواتك، استخدمها لما تكون هي الحل الأنسب للمشكلة.
- ابدأ صغيرًا: مش ضروري تحول كل نظامك لـ GraphQL مرة واحدة. ممكن تبدأ بميزة جديدة أو بجزء معقد من تطبيقك الحالي وتعمله بـ GraphQL. كثير من الشركات بتشغل GraphQL و REST جنبًا إلى جنب.
- انتبه للأداء والأمان: بما أن العميل صار عنده قوة كبيرة، ممكن يكتب استعلامات معقدة جدًا تسبب ضغطًا هائلاً على الخادم (مثلاً يطلب 10 مستويات متداخلة من البيانات). لازم تستخدم أدوات مثل تحديد عمق الاستعلام (query depth limiting) أو تحليل تكلفة الاستعلام (query cost analysis) عشان تحمي الخادم من الاستعلامات الخبيثة أو السيئة.
- التخزين المؤقت (Caching): التخزين المؤقت في GraphQL مختلف شوي عن REST. في REST، بتقدر تخزن استجابة
GET /users/1كاملة. في GraphQL، بما أنه كل الطلبات بتروح على نقطة نهاية واحدة (POST /graphql)، التخزين على مستوى HTTP بصير أصعب. الحل بيكون في التخزين على مستوى العميل (باستخدام مكتبات مثل Apollo Client أو Relay) اللي بتفهم استجابات GraphQL وبتقدر تخزن الكيانات (entities) بشكل ذكي.
الخلاصة: هل يجب أن أستخدم GraphQL؟ 💡
إذا كنت بتعاني من نفس المشاكل اللي عانينا منها: واجهات أمامية معقدة بتحتاج بيانات من مصادر متعددة، بطء في التطبيق بسبب كثرة الطلبات، وصداع مستمر بين فرق الواجهات الأمامية والخلفية… فالجواب هو نعم، على الأقل لازم تجربها.
GraphQL نقلة نوعية في طريقة تفكيرنا ببناء الـ APIs. هي بتعطي القوة والمرونة للواجهات الأمامية، وبتقلل الاعتمادية على الفريق الخلفي لكل تغيير صغير، والأهم من كل هاد، بتحسن تجربة المستخدم النهائية بتطبيقات أسرع وأكثر استجابة.
لا تخاف من تعلم إشي جديد. في البداية يمكن تحسها معقدة، لكن الفوائد اللي رح تجنيها على المدى الطويل تستحق كل دقيقة بتستثمرها في تعلمها. يلا يا جماعة، شدوا الهمة وجربوها!