GraphQL vs REST في C#: معركة العمالقة أم اختيار الأداة المناسبة للمهمة؟
\n\nمرحبًا أيها الزملاء! إذا كنت تكتب بلغة C# وتنشئ واجهات API، فمن المؤكد أن سؤال "REST أم GraphQL؟" قد طرأ على ذهنك أكثر من مرة. إنه مثل الجدل حول أيهما أفضل: المطرقة أم مفك البراغي الكهربائي. كلاهما أدوات، لكن لمهام مختلفة. في هذه المقالة، لن نتعصب لصالح تقنية معينة، بل سنناقش بواقعية، مع أمثلة كود بلغة C#، متى يكون GraphQL هو الخيار الرابح، ومتى يبقى REST القديم الجيد هو الملك.
\n\nنحن فريق PythonLib، ورغم أن الاسم يحمل "Python"، إلا أننا نحب نظام .NET و C#. اليوم سنلقي نظرة على عالم واجهات API بلغة C# ونتعرف على كيفية اتخاذ القرار الصحيح. هيا بنا!
\n\nشرح مبسط: REST و GraphQL لمطور C#
\n\nREST (Representational State Transfer) هو أسلوب معماري نعرفه جميعًا ونحبه. في C#، على الأرجح تستخدم ASP.NET Core Web API. تقوم بإنشاء وحدات تحكم (Controllers) ونقاط نهاية (Endpoints)، وكل نقطة نهاية تعيد بنية بيانات ثابتة. تريد الحصول على مستخدم؟ تذهب إلى /api/users/1. تريد طلباته؟ تذهب إلى /api/users/1/orders. بسيط، يمكن التنبؤ به، لكنه غالبًا ما يؤدي إلى استقبال بيانات زائدة (over-fetching) أو الحاجة إلى عدة طلبات (under-fetching).
GraphQL هي لغة استعلام طورتها فيسبوك. في عالم C#، يتم تنفيذها عبر مكتبات مثل Hot Chocolate (الأكثر شيوعًا) أو GraphQL.NET. لديك نقطة نهاية واحدة (عادةً /graphql)، ويقرر العميل الحقول التي يحتاجها. تريد اسم المستخدم وأول طلب فقط؟ تفضل! لا مزيد من البيانات الزائدة.
مقارنة عملية: كود C#
\n\nدعنا ننتقل من النظرية إلى التطبيق. لنفترض أن لدينا نموذجًا بسيطًا: User و Order.
// نماذج البيانات\npublic class User\n{\n public int Id { get; set; }\n public string Name { get; set; }\n public string Email { get; set; }\n public string Phone { get; set; }\n public List<Order> Orders { get; set; }\n}\n\npublic class Order\n{\n public int Id { get; set; }\n public string ProductName { get; set; }\n public decimal Price { get; set; }\n public DateTime CreatedAt { get; set; }\n}\n\n\nمثال على REST API باستخدام ASP.NET Core
\n\n[ApiController]\n[Route("api/[controller]")]\npublic class UsersController : ControllerBase\n{\n [HttpGet("{id}")]\n public async Task<ActionResult<User>> GetUser(int id)\n {\n // لنفترض أن البيانات تأتي من قاعدة البيانات\n var user = await _dbContext.Users.FindAsync(id);\n if (user == null) return NotFound();\n return Ok(user); // يعيد جميع الحقول: Id, Name, Email, Phone, Orders\n }\n}\n\n\nالمشكلة؟ إذا كان العميل يحتاج فقط الاسم والبريد الإلكتروني، فسيظل يتلقى Phone و Orders. وإذا كان بحاجة إلى الطلبات، فعليه إجراء طلب منفصل إلى /api/users/1/orders.
مثال على GraphQL API باستخدام Hot Chocolate
\n\npublic class Query\n{\n public async Task<User> GetUser(int id, [Service] AppDbContext dbContext)\n {\n return await dbContext.Users.FindAsync(id);\n }\n}\n\n// التسجيل في Program.cs\nbuilder.Services\n .AddGraphQLServer()\n .AddQueryType<Query>();\n\n\nالآن يمكن للعميل طلب ما يحتاجه بالضبط:
\n\n// استعلام GraphQL\n{\n user(id: 1) {\n name\n email\n orders {\n productName\n price\n }\n }\n}\n\n\nسيحتوي الرد فقط على name و email ومصفوفة orders مع الحقول productName و price. لا بيانات زائدة. رائع!
متى يفوز REST في C# (وهذا طبيعي)
\n\n1. البساطة والتخزين المؤقت. يمكن تخزين REST API مؤقتًا بسهولة على مستوى HTTP (CDN، بروكسي). يمكن تخزين طلب GET على /api/products مؤقتًا، وهذا يفيد الجميع.