GraphQL API باستخدام Rust: دليل عملي خطوة بخطوة
GraphQL هي لغة استعلام قوية لواجهات API، تتيح للعملاء طلب البيانات التي يحتاجونها فقط. وبالاقتران مع أداء Rust وأمانها، تحصل على حل مثالي لخدمات الويب عالية التحميل. في هذا الدليل التعليمي، سننشئ GraphQL API كاملاً من الصفر باستخدام الكريت async-graphql.
لماذا Rust؟ إنها توفر تكلفة صفرية للتجريدات، وأمان الذاكرة، وسرعة مذهلة. GraphQL على Rust يعمل أسرع من نظائره على JavaScript أو Python، وهو أمر حاسم لأنظمة الإنتاج.
1. إعداد المشروع والتبعيات
لنبدأ بإنشاء مشروع Rust جديد. افتح الطرفية ونفذ:
cargo new graphql_rust_apicd graphql_rust_apiالآن أضف التبعيات الضرورية في ملف Cargo.toml:
[dependencies]async-graphql = "7.0"async-graphql-actix-web = "7.0"actix-web = "4"actix-cors = "0.7"serde = { version = "1.0", features = ["derive"] }tokio = { version = "1", features = ["full"] }توفر هذه الكريتات كل ما هو ضروري: async-graphql — نواة GraphQL، actix-web — خادم الويب، serde — تسلسل البيانات.
2. إنشاء نموذج البيانات والمخطط
لنحدد نموذجاً بسيطاً لواجهة API الخاصة بنا — الكتب. سننشئ الهياكل ومخطط GraphQL:
use async_graphql::*;use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, Serialize, Deserialize, SimpleObject)]struct Book { id: ID, title: String, author: String, year: i32,}
#[derive(Clone, Debug, Serialize, Deserialize, InputObject)]struct BookInput { title: String, author: String, year: i32,}لاحظ السمات #[derive(SimpleObject)] و #[derive(InputObject)]. الأولى تولد تلقائياً نوع GraphQL للإخراج، والثانية لإدخال البيانات.
3. تنفيذ المحللات (الاستعلام والتحوير)
المحللات هي دوال تعالج الاستعلامات. لننشئ هيكل التخزين والمحللات:
use std::sync::Mutex;
struct QueryRoot;
#[Object]impl QueryRoot { async fn books(&self, ctx: &Context<'_>) -> Vec<Book> { let storage = ctx.data_unchecked::<Mutex<Vec<Book>>>(); storage.lock().unwrap().clone() }
async fn book(&self, ctx: &Context<'_>, id: ID) -> Option<Book> { let storage = ctx.data_unchecked::<Mutex<Vec<Book>>>(); storage.lock().unwrap().iter().find(|b| b.id == id).cloned() }}
struct MutationRoot;
#[Object]impl MutationRoot { async fn add_book(&self, ctx: &Context<'_>, input: BookInput) -> Book { let mut storage = ctx.data_unchecked::<Mutex<Vec<Book>>>() .lock().unwrap(); let book = Book { id: ID::from(storage.len() + 1), title: input.title, author: input.author, year: input.year, }; storage.push(book.clone()); book }}نستخدم Mutex للوصول الآمن للخيوط إلى البيانات. في مشروع حقيقي، ستقوم بتوصيل قاعدة بيانات، لكن للدليل التعليمي هذا هو الخيار المثالي.
4. تجميع وتشغيل الخادم
الخطوة النهائية — دمج كل شيء في خادم يعمل. لننشئ نقطة الدخول:
use actix_web::{guard, web, App, HttpServer, HttpRequest, HttpResponse};use actix_cors::Cors;use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
async fn index( schema: web::Data<Schema<QueryRoot, MutationRoot, EmptySubscription>>, req: GraphQLRequest,) -> GraphQLResponse { schema.execute(req.into_inner()).await.into()}
#[actix_web::main]async fn main() -> std::