لماذا Rust هو المرشح المثالي للعمل مع API؟
\n\nإذا كنت لا تزال تعتقد أن Rust مخصص فقط للبرمجة النظامية واستبدال لغة C++، فأنت متأخر كثيرًا عن الواقع. لقد اقتحمت Rust عالم تطوير الويب وخدمات الواجهة الخلفية بثقة. ولم تقتحم فقط، بل احتلت مكانة حيث تكون السرعة والموثوقية وأمان الذاكرة مهمة.
\n\nلكن لنكن صادقين: العمل مع API بلغة Rust ليس مجرد "أخذ مكتبة جاهزة ونسيان الأمر". إنه فن التوازن بين قوة اللغة وسهولة التطوير. في هذه المقالة، سنستعرض ثلاثة مناهج رئيسية: REST وGraphQL وgRPC. كل منها جيد بطريقته، والاختيار يعتمد على مهمتك. هيا بنا!
\n\nREST بلغة Rust: الكلاسيكية التي تعمل
\n\nلا يزال REST هو النمط الأكثر شيوعًا لـ API. إنه بسيط وبديهي ومدعوم من قبل الجميع حرفيًا. بالنسبة لـ Rust، هناك العديد من الأطر الممتازة، لكن الملك هنا هو actix-web. إنه سريع وغير متزامن وله نظام بيئي ممتاز.
كتابة خدمة REST بسيطة باستخدام actix-web
\n\nلنقم بإنشاء خدمة مصغرة لإدارة قائمة المهام (todo). سنحتاج إلى actix-web وserde للتسلسل وtokio كبيئة تشغيل غير متزامنة.
// Cargo.toml\n// [dependencies]\n// actix-web = "4"\n// serde = { version = "1", features = ["derive"] }\n// serde_json = "1"\n// tokio = { version = "1", features = ["full"] }\n\nuse actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};\nuse serde::{Deserialize, Serialize};\nuse std::sync::Mutex;\n\n#[derive(Serialize, Deserialize, Clone)]\nstruct Task {\n id: u32,\n title: String,\n completed: bool,\n}\n\nstruct AppState {\n tasks: Mutex<Vec<Task>>,\n}\n\n#[get("/tasks")]\nasync fn get_tasks(data: web::Data<AppState>) -> impl Responder {\n let tasks = data.tasks.lock().unwrap();\n HttpResponse::Ok().json(tasks.clone())\n}\n\n#[post("/tasks")]\nasync fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> impl Responder {\n let mut tasks = data.tasks.lock().unwrap();\n let new_id = tasks.len() as u32 + 1;\n let task = Task {\n id: new_id,\n title: body.title.clone(),\n completed: false,\n };\n tasks.push(task.clone());\n HttpResponse::Created().json(task)\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n let state = web::Data::new(AppState {\n tasks: Mutex::new(vec![]),\n });\n\n HttpServer::new(move || {\n App::new()\n .app_data(state.clone())\n .service(get_tasks)\n .service(create_task)\n })\n .bind("127.0.0.1:8080")?\n .run()\n .await\n}\n\n\nجمال هذا الكود هو أنه آمن من حيث الذاكرة ولا يتطلب جامع قمامة. يعالج actix-web آلاف الطلبات في الثانية على خيط واحد، باستخدام آليات Rust غير المتزامنة. جرب تشغيله - ستفاجأ بمدى سرعته حتى على الأجهزة الضعيفة.
متى لا يكون REST الخيار الأفضل؟
\n\nREST مناسب تمامًا لعمليات CRUD، ولكن إذا كنت بحاجة إلى طلب بيانات بمرونة (مثل الموارد المتداخلة)، يبدأ REST في طلب العديد من نقاط النهاية. هنا يظهر GraphQL على المسرح.
\n\nGraphQL بلغة Rust: مرونة بدون ألم
\n\nيسمح GraphQL للعميل بطلب البيانات التي يحتاجها بالضبط. بالنسبة لـ Rust، الخيار الأفضل هو async-graphql. إنه يتكامل بشكل وثيق مع actix-web ويوفر DSL قوي لوصف المخططات.
مثال: API لمكتبة كتب
\n\nلنفترض أننا نريد منح العملاء القدرة على طلب الكتب والمؤلفين. مع GraphQL، يتم ذلك بمخطط واحد.
\n\n// Cargo.toml\n// [dependencies]\n// async-graphql = "7"\n// async-graphql-actix-web = "7"\n// actix-web = "4"\n// serde = { version = "1", features = ["derive"] }\n\nuse async_graphql::{Context, EmptyMutatio