Einführung in die GraphQL API mit Rust
Moderne Webentwicklung erfordert flexible und leistungsstarke APIs. GraphQL ist eine Abfragesprache, die es Clients ermöglicht, genau die Daten zu erhalten, die sie benötigen, ohne Redundanz. Rust wiederum bietet hohe Leistung und Speichersicherheit. In diesem Tutorial erstellen wir einen einfachen, aber vollwertigen GraphQL-Server mit Rust unter Verwendung der Bibliothek async-graphql. Sie lernen, wie Sie das Projekt einrichten, Datentypen definieren, Resolver schreiben und den Server starten. Der Artikel richtet sich an Anfänger, die mit den Grundlagen von Rust vertraut sind.
Projekteinrichtung und Installation der Abhängigkeiten
Zunächst erstellen wir ein neues Rust-Projekt mit Cargo:
cargo new graphql-rust-tutorialcd graphql-rust-tutorialÖffnen Sie die Datei Cargo.toml und fügen Sie die folgenden Abhängigkeiten hinzu:
[dependencies]async-graphql = "7.0"async-graphql-actix-web = "7.0"actix-web = "4"serde = { version = "1.0", features = ["derive"] }tokio = { version = "1", features = ["full"] }Erläuterung der Bibliotheksauswahl:
- async-graphql — die Hauptbibliothek für die Arbeit mit GraphQL in Rust.
- async-graphql-actix-web — Integration mit dem Web-Framework Actix-web.
- actix-web — ein beliebter asynchroner HTTP-Server.
- serde — für die Serialisierung/Deserialisierung von Daten.
- tokio — asynchrone Laufzeitumgebung.
Führen Sie cargo build aus, um sicherzustellen, dass alle Abhängigkeiten korrekt installiert sind.
Definition des GraphQL-Schemas und der Resolver
Wir erstellen ein einfaches Datenmodell – ein Buch. Definieren wir die Struktur und die Resolver für Abfragen und Mutationen.
Erstellung des Datenmodells
Erstellen Sie die Datei src/models.rs und fügen Sie den folgenden Code hinzu:
use async_graphql::*;
#[derive(SimpleObject)]pub struct Book { pub id: ID, pub title: String, pub author: String,}
#[derive(InputObject)]pub struct NewBook { pub title: String, pub author: String,}Hier generiert SimpleObject automatisch den GraphQL-Typ für die Struktur Book, und InputObject für die Eingabedaten der Mutation.
Erstellung des Speichers und der Resolver
Erstellen wir die Datei src/resolvers.rs, die die Logik für Abfragen und Mutationen enthält:
use std::sync::Mutex;use async_graphql::*;use crate::models::{Book, NewBook};
pub struct QueryRoot;
#[Object]impl QueryRoot { async fn books(&self) -> Vec<Book> { let storage = STORAGE.lock().unwrap(); storage.clone() }
async fn book(&self, id: ID) -> Option<Book> { let storage = STORAGE.lock().unwrap(); storage.iter().find(|b| b.id == id).cloned() }}
pub struct MutationRoot;
#[Object]impl MutationRoot { async fn add_book(&self, input: NewBook) -> Book { let mut storage = STORAGE.lock().unwrap(); let new_book = Book { id: ID::from(format!("book_{}", storage.len() + 1)), title: input.title, author: input.author, }; storage.push(new_book.clone()); new_book }}
lazy_static::lazy_static! { static ref STORAGE: Mutex<Vec<Book>> = Mutex::new(vec![]);}Beachten Sie: Wir verwenden Mutex für den threadsicheren Zugriff auf den Speicher. In einem echten Projekt sollten Sie besser eine Datenbank verwenden.
Zusammenbau des Schemas
Jetzt fügen wir alles in der Datei src/main.rs zusammen:
use actix_web::{web, App, HttpServer, HttpResponse, middleware};use async_graphql::{http::GraphiQLSource, EmptySubscription, Schema};use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};use std::sync::Arc;
mod models;mod resol