GraphQL API باستخدام Rust: دليل خطوة بخطوة لبناء خلفية سريعة

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

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

ابدأ الدورة

مقدمة إلى GraphQL API باستخدام Rust



تتطلب تطوير الويب الحديث أداءً عاليًا ومرونة في التعامل مع البيانات. Rust هي لغة برمجة أنظمة، معروفة بسرعتها وأمان الذاكرة، بينما GraphQL هي لغة استعلام قوية تتيح للعملاء الحصول على البيانات المطلوبة فقط. دمج هاتين التقنيتين يوفر APIs سريعة بشكل لا يصدق وسهلة الاستخدام.



في هذا البرنامج التعليمي، سنقوم بإنشاء GraphQL API كامل باستخدام Rust مع استخدام الكريتات الشهيرة Juniper (لتنفيذ GraphQL) و Actix-web(لخادم HTTP). ستتعلم كيفية إعداد المشروع، تعريف المخطط، تنفيذ المحللين (resolvers)، ومعالجة الطلبات.



المقالة موجهة للمطورين الذين لديهم معرفة أساسية بـ Rust والمفاهيم الأساسية لواجهات برمجة التطبيقات (APIs). سنستخدم Rust 2021 edition وأحدث الإصدارات المستقرة من التبعيات.



الخطوة 1: إعداد المشروع والتبعيات



لنبدأ بإنشاء مشروع Rust جديد. افتح الطرفية (terminal) ونفذ:



cargo new graphql-rust-apicd graphql-rust-api


ثم افتح ملف Cargo.toml وأضف التبعيات التالية:



[dependencies]actix-web = "4"actix-rt = "2"juniper = "0.16"serde = { version = "1.0", features = ["derive"] }tokio = { version = "1", features = ["full"] }


  • actix-web — إطار عمل ويب غير متزامن (async) لـ Rust.
  • juniper — مكتبة لتنفيذ GraphQL على Rust.
  • serde — للتسلسل وإلغاء التسلسل (serialization/deserialization) للبيانات.
  • tokio — بيئة تشغيل غير متزامنة (async runtime)، ضرورية لـ Actix.


بعد تحديث التبعيات، نفذ cargo build للتأكد من أن كل شيء يتم تثبيته بشكل صحيح.



الخطوة 2: تعريف نموذج البيانات ومخطط GraphQL



كمثال، سنقوم بإنشاء نموذج بسيط "كتاب" مع الحقول: id، العنوان، المؤلف، وسنة النشر. لننشئ ملف src/models.rs:



use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]pub struct Book { pub id: i32, pub title: String, pub author: String, pub year: i32,}


الآن سنقوم بتعريف مخطط GraphQL. لننشئ src/graphql.rs:



use juniper::{FieldResult, GraphQLObject, GraphQLInputObject, RootNode};use crate::models::Book;

// نوع GraphQL للكتاب (الإخراج)#[derive(GraphQLObject)]#[graphql(description = "كتاب")]pub struct BookGQL { pub id: i32, pub title: String, pub author: String, pub year: i32,}

// نوع الإدخال لإنشاء كتاب#[derive(GraphQLInputObject)]#[graphql(description = "بيانات الإدخال لكتاب جديد")]pub struct NewBook { pub title: String, pub author: String, pub year: i32,}

// "قاعدة بيانات" في الذاكرةuse std::sync::Mutex;lazy_static::lazy_static! { static ref BOOKS: Mutex<Vec<Book>> = Mutex::new(vec![ Book { id: 1, title: "Rust في العمل".to_string(), author: "تيم مكنمارا".to_string(), year: 2021 }, Book { id: 2, title: "البرمجة بلغة Rust".to_string(), author: "جيم بلاندي".to_string(), year: 2020 }, ]);}

// الاستعلام الجذري (Query)pub struct QueryRoot;

#[juniper::object]impl QueryRoot { fn books() -> FieldResult<Vec<BookGQL>> { let books = BOOKS.lock().unwrap(); Ok(books.iter().map(|b| BookGQL { id: b.id, title: b.title.clone(), author: b.author.clone(), year: b.year, }).collect()) }

fn book(id: i32) -> FieldResult<BookGQL> { let books = BOOKS.lock().unwrap(); let book = books.iter().find(|b| b.id == id) .ok_or_else(|| juniper::FieldError::new("الكتاب غير موجود", graphql_value!({"code": "NOT_FOUND"})))?; Ok(BookGQL { id: book.id,

المدونات

توصيات الكتب