مقدمة: لماذا Rust و GraphQL زوج مثالي
\nتتطلب تطوير الويب الحديث أداءً عاليًا وكتابة صارمة للأنواع. Rust، المعروفة بأمان الذاكرة وسرعتها، أصبحت خيارًا ممتازًا لبناء الخوادم الخلفية. من ناحية أخرى، توفر GraphQL طريقة مرنة وفعالة للعمل مع البيانات. في هذا الدليل التعليمي، سنقوم بإنشاء GraphQL API كامل باستخدام Rust، باستخدام المكتبة async-graphql وإطار العمل actix-web. ستتعلم كيفية تعريف المخططات، كتابة المحللين (Resolvers)، ومعالجة الاستعلامات.
في نهاية المقال، سيكون لديك خادم عامل جاهز للتكامل مع المشاريع الحقيقية. جميع أمثلة الكود تم اختبارها وجاهزة للتشغيل.
\n\nإعداد المشروع وتثبيت التبعيات
\nللبدء، سنقوم بإنشاء مشروع Rust جديد. تأكد من تثبيت Rust (يُوصى بالإصدار 1.75+).
\ncargo new graphql-rust-api\ncd graphql-rust-api\nافتح ملف Cargo.toml وأضف التبعيات التالية:
[dependencies]\nactix-web = "4"\nactix-rt = "2"\nasync-graphql = "7"\nasync-graphql-actix-web = "7"\nserde = { version = "1", features = ["derive"] }\ntokio = { version = "1", features = ["macros"] }\nقم بتشغيل cargo build لتحميل وتجميع جميع التبعيات. قد يستغرق ذلك بضع دقائق عند التشغيل الأول.
هيكل المشروع
\nأنشئ هيكل المجلدات والملفات التالي:
\nsrc/\n├── main.rs\n├── schema.rs\n├── models.rs\n└── resolvers.rs\nسنقوم بملء كل ملف تدريجيًا.
\n\nتعريف النماذج ومخطط GraphQL
\nلنبدأ بإنشاء نموذج بيانات بسيط. في ملف models.rs، سنصف هيكل User:
use serde::{Deserialize, Serialize};\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct User {\n pub id: u32,\n pub name: String,\n pub email: String,\n}\nالآن ننتقل إلى schema.rs. هنا سنقوم بتعريف كائنات GraphQL والاستعلامات:
use async_graphql::{Context, Object, SimpleObject};\nuse crate::models::User;\nuse crate::resolvers::get_all_users;\n\n#[derive(SimpleObject)]\npub struct UserObject {\n pub id: u32,\n pub name: String,\n pub email: String,\n}\n\npub struct QueryRoot;\n\n#[Object]\nimpl QueryRoot {\n async fn users(&self, ctx: &Context<'_>) -> Vec<UserObject> {\n let users = get_all_users(ctx).await;\n users.into_iter().map(|u| UserObject {\n id: u.id,\n name: u.name,\n email: u.email,\n }).collect()\n }\n}\nلاحظ استخدام #[derive(SimpleObject)] - وهو ماكرو من async-graphql يقوم تلقائيًا بإنشاء نوع GraphQL.
المحللون (Resolvers): منطق الأعمال
\nفي ملف resolvers.rs، سنقوم بتنفيذ دالة جلب البيانات. للتبسيط، سنستخدم قائمة ثابتة:
use async_graphql::Context;\nuse crate::models::User;\n\npub async fn get_all_users(_ctx: &Context<'_>) -> Vec<User> {\n vec![\n User { id: 1, name: "Alice".into(), email: "alice@example.com".into() },\n User { id: 2, name: "Bob".into(), email: "bob@example.com".into() },\n User { id: 3, name: "Charlie".into(), email: "charlie@example.com".into() },\n ]\n}\nفي مشروع حقيقي، قد يكون هنا استدعاء لقاعدة بيانات أو API خارجي.
\n\nبناء الخادم باستخدام Actix-web
\nالآن الجزء الأهم - تجميع كل شيء معًا في ملف main.rs:
use actix_web::{web, App, HttpServer, HttpResponse};\nuse async_graphql::{Schema, EmptyMutation, EmptySubscription};\nuse async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};\nuse crate::schema::QueryRoot;\n\nmod models;\nmod schema;\nmod resolvers;\n\ntype AppSchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;\n\nasync fn graphql_handler(\n schema