GraphQL مقابل REST: مبارزة كان يجب أن تبدأ بالأمس
\n\nموقف مألوف: أنت تكتب مشروعًا جديدًا على JavaScript (أو Node.js)، وفجأة يطرح السؤال — ماذا تستخدم للتواصل بين الواجهة الأمامية والخلفية؟ REST الكلاسيكي أم GraphQL العصري؟ الجدل حول هذا الموضوع لا يهدأ، ولكل مطور رأيه "الحديدي".
\n\nلنكن صادقين: REST هو الصديق القديم الجيد الذي لا يخذلك أبدًا. GraphQL هو الوافد الجديد الجريء الذي يعد بحل جميع مشاكلك. ولكن كيف تختار؟ سأريكم ليس فقط جداول مقارنة جافة، بل أمثلة قتالية حقيقية على JavaScript. سترون كيف يبدو الكود، وأين تظهر المزالق، والأهم من ذلك — متى نستخدم ماذا.
\n\nبالمناسبة، إذا كنت تطور على Python (وهذا الموقع عن Python، لا تنسَ)، فإن مبادئ الاختيار بين هاتين التقنيتين متشابهة جدًا. لكننا اليوم نتحدث عن JavaScript — اللغة التي تسود على كل من العميل والخادم.
\n\nشرح سريع: REST و GraphQL في 60 ثانية
\n\nREST (نقل الحالة التمثيلية)
\nهذا أسلوب معماري حيث يكون لكل مورد (مستخدم، منشور، تعليق) عنوان URL خاص به. تقوم بإجراء طلب GET إلى /api/users/1 — تحصل على المستخدم. تريد منشوراته؟ اذهب إلى /api/users/1/posts. كل شيء بسيط، قابل للتوقع، ولكن... أحيانًا كثير الكلام.
GraphQL
\nهي لغة استعلام وبيئة تشغيل. لديك نقطة نهاية واحدة (عادةً /graphql)، وأنت بنفسك تقرر البيانات التي تريد الحصول عليها. لا مزيد من over-fetching (عندما تأتي 50 حقلاً وأنت تحتاج 3) ولا under-fetching (عندما تحتاج إلى 5 طلبات لتجميع صفحة).
مقارنة قتالية: نكتب كودًا على JavaScript
\n\nلنتخيل مهمة نموذجية: نحتاج إلى عرض صفحة ملف مستخدم مع أحدث ثلاثة منشورات له. دعنا نقارن بين النهجين.
\n\nمثال REST API على Express (Node.js)
\n\n// خادم REST على Express\nconst express = require('express');\nconst app = express();\n\napp.get('/api/users/:id', (req, res) => {\n // نعيد جميع بيانات المستخدم\n const user = users.find(u => u.id === req.params.id);\n res.json(user); // { id, name, email, phone, address, ... }\n});\n\napp.get('/api/users/:id/posts', (req, res) => {\n // نعيد جميع منشورات المستخدم\n const posts = posts.filter(p => p.userId === req.params.id);\n res.json(posts); // [{ id, title, body, createdAt, ... }]\n});\n\n// كود العميل (الواجهة الأمامية باستخدام fetch)\nasync function loadProfile(userId) {\n // الطلب الأول — نحصل على المستخدم (يأتي الكثير من الزوائد)\n const userResponse = await fetch(`/api/users/${userId}`);\n const user = await userResponse.json();\n \n // الطلب الثاني — نحصل على المنشورات (مرة أخرى بيانات زائدة)\n const postsResponse = await fetch(`/api/users/${userId}/posts`);\n const allPosts = await postsResponse.json();\n \n // نأخذ فقط 3 منشورات أخيرة\n const latestPosts = allPosts.slice(0, 3);\n \n return { user, posts: latestPosts };\n}\n\nالمشكلة واضحة: لقد حصلنا على الكثير من البيانات الزائدة (over-fetching). لماذا نحتاج email و phone و address إذا كنا نعرض فقط الاسم والصورة الرمزية؟ وقد يكون هناك 100 منشور، ونحتاج فقط 3.
\n\nمثال GraphQL على Apollo Server (Node.js)
\n\n// مخطط GraphQL\nconst typeDefs = `\n type User {\n id: ID!\n name: String\n avatar: String\n posts(limit: Int): [Post]\n }\n\n type Post {\n id: ID!\n title: String\n body: String\n }\n\n type Query {\n user(id: ID!): User\n }\n`;\n\n// المحللات (منطق الحصول على البيانات)\nconst resolvers = {\n Query: {\n user: (parent, { id }) => users.find(u => u.id === id),\n },\n User: {\n posts: (parent, { limit }) => {\n let result = posts.filter(p => p.userId === parent.id);\n if (limit) result = result.slice(0, limit);\n