GraphQL مقابل REST في Rust: أيهما تختار للباك إند في 2025؟

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: REST أم GraphQL — جدل أبدي سنحسمه على Rust



إذا كنت تكتب بلغة Rust وتختار بنية API الخاصة بك، فبالتأكيد تساءلت: ماذا تختار — REST الكلاسيكي أم GraphQL العصري؟ كلا النهجين لديهما جيش من المعجبين وجبل من المنتقدين. يُتهم REST بزيادة البيانات و"ثرثرة" العميل مع الخادم. ويُتهم GraphQL بتعقيد التخزين المؤقت واستعلامات "جحيمية" لقاعدة البيانات.



لكن دعونا نواجه الحقيقة: Rust ليست JavaScript ولا Python. فلسفتها هي التجريدات ذات التكلفة الصفرية (zero-cost abstractions) والأداء الأقصى. فهل يعني هذا أن أحد النهجين يناسب نظام Rust البيئي بشكل أفضل؟ دعونا نكتشف ذلك من خلال أمثلة حقيقية مع الكود.



في هذه المقالة، لن نكتفي بسرد الإيجابيات والسلبيات. سنكتب خدمتين صغيرتين باستخدام actix-web (REST) و async-graphql، ونقارن أداءهما، وصعوبة التطوير، وسهولة الصيانة. هيا بنا!



شرح سريع: REST و GraphQL في 30 ثانية



REST (نقل الحالة التمثيلية) هو نمط معماري حيث كل مورد (مستخدم، مقال، طلب) له عنوان URL خاص به. تحصل على البيانات عبر GET، وتنشئها عبر POST، وتحدثها عبر PUT/PATCH. كل شيء بسيط وقابل للتوقع. لكن غالبًا ما تحصل على بيانات أكثر مما تحتاج، أو تقوم بعدة استعلامات لجمع الكيانات المرتبطة.



GraphQL هي لغة استعلام وبيئة تشغيل. العميل هو من يقرر الحقول التي يحتاجها. استعلام واحد — وتحصل بالضبط على ما طلبت. لكن هذه المرونة تأتي بتكلفة تعقيد التنفيذ على الخادم ومشاكل محتملة في الأداء.



في Rust، تم تنفيذ كلا النهجين على أعلى مستوى بفضل نظام الأنواع (type system) والمعالجة غير المتزامنة (asynchrony). لكن دعونا نتعمق أكثر.



REST على Rust: الكلاسيكية مع actix-web



لنبدأ مع REST المألوف. على سبيل المثال، لنأخذ مدونة بسيطة: يوجد مستخدمون، مقالات، وتعليقات. في REST، سنحتاج إلى إنشاء عدة نقاط نهاية (endpoints).



use actix_web::{web, App, HttpServer, Responder, HttpResponse};use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]struct Article { id: i32, title: String, content: String, author_id: i32,}

async fn get_article(path: web::Path<i32>) -> impl Responder { let article_id = path.into_inner(); // لنفترض أننا نجلب المقال من قاعدة البيانات let article = Article { id: article_id, title: "GraphQL vs REST".to_string(), content: "مقارنة مفصلة...".to_string(), author_id: 1, }; HttpResponse::Ok().json(article)}

async fn get_articles_by_author(path: web::Path<i32>) -> impl Responder { let author_id = path.into_inner(); // قائمة مقالات المؤلف let articles = vec![ Article { id: 1, title: "مقال 1".to_string(), content: "نص...".to_string(), author_id }, Article { id: 2, title: "مقال 2".to_string(), content: "نص...".to_string(), author_id }, ]; HttpResponse::Ok().json(articles)}

#[actix_web::main]async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .route("/articles/{id}", web::get().to(get_article)) .route("/authors/{id}/articles", web::get().to(get_articles_by_author)) }) .bind("127.0.0.1:8080")? .run() .await}


كل شيء يبدو نظيفًا وواضحًا. إيجابيات REST على Rust:

  • سهولة التخزين المؤقت على مستوى HTTP (ETag, Cache-Control).
  • هيكل شفاف — كل نقطة نهاية تقوم بشيء واحد.
  • تكامل سهل مع أي عميل (curl, Postman, متصفح).


لكن هناك سلبيات:

  • مشكلة استعلامات N+1. للحصول على المقال والمؤلف، تحتاج إلى استعلامين. أو إنشاء نقطة نهاية منفصلة مع JOIN.
  • جلب بيانات زائدة (Over-fetching).<

المدونات

توصيات الكتب