Introduction : pourquoi Rust et GraphQL forment une paire idéale
\nLe développement web moderne exige des performances élevées et un typage strict. Rust, connu pour sa sécurité mémoire et sa rapidité, devient un excellent choix pour créer des backends. GraphQL, quant à lui, offre une manière flexible et efficace de travailler avec les données. Dans ce tutoriel, nous allons créer une API GraphQL complète en Rust, en utilisant la bibliothèque async-graphql et le framework actix-web. Vous apprendrez à définir des schémas, écrire des résolveurs et traiter des requêtes.
À la fin de cet article, vous disposerez d'un serveur fonctionnel, prêt à être intégré dans des projets réels. Tous les exemples de code sont vérifiés et prêts à être exécutés.
\n\nConfiguration du projet et installation des dépendances
\nPour commencer, créons un nouveau projet Rust. Assurez-vous d'avoir Rust installé (version recommandée 1.75+).
\ncargo new graphql-rust-api\ncd graphql-rust-api\nOuvrez Cargo.toml et ajoutez les dépendances suivantes :
[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"] }\nExécutez cargo build pour télécharger et compiler toutes les dépendances. Cela peut prendre quelques minutes lors de la première exécution.
Structure du projet
\nCréez la structure de dossiers et fichiers suivante :
\nsrc/\n├── main.rs\n├── schema.rs\n├── models.rs\n└── resolvers.rs\nNous allons remplir chaque fichier progressivement.
\n\nDéfinition des modèles et du schéma GraphQL
\nCommençons par créer un modèle de données simple. Dans models.rs, décrivons la structure 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}\nPassons maintenant à schema.rs. Ici, nous définirons les objets GraphQL et les requêtes :
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}\nNotez l'utilisation de #[derive(SimpleObject)] — c'est une macro d'async-graphql qui génère automatiquement le type GraphQL.
Résolveurs : logique métier
\nDans le fichier resolvers.rs, implémentons la fonction de récupération des données. Pour simplifier, nous utiliserons une liste statique :
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}\nDans un projet réel, cela pourrait être un appel à une base de données ou à une API externe.
\n\nAssemblage du serveur avec Actix-web
\nVient maintenant l'étape la plus importante : assembler le tout dans 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