Introduction : REST ou GraphQL — un débat éternel que nous allons trancher avec Rust
Si vous codez en Rust et choisissez une architecture pour votre API, vous vous êtes sûrement demandé : que choisir — le classique REST ou le tendance GraphQL ? Les deux approches ont une armée de fans et une montagne de détracteurs. On reproche à REST la redondance des données et le "bavardage" entre le client et le serveur. À GraphQL, on reproche la complexité du cache et les requêtes "infernales" à la base de données.
Mais regardons la vérité en face : Rust n'est ni JavaScript ni Python. Sa philosophie repose sur les zero-cost abstractions et une performance maximale. Cela signifie-t-il qu'une des approches est mieux adaptée à l'écosystème Rust ? Analysons cela avec des exemples concrets de code.
Dans cet article, nous ne nous contenterons pas de lister les avantages et les inconvénients. Nous allons écrire deux microservices avec actix-web (REST) et async-graphql, comparer leurs performances, leur complexité de développement et leur facilité de maintenance. C'est parti !
Petit rappel : REST et GraphQL en 30 secondes
REST (Representational State Transfer) est un style architectural où chaque ressource (utilisateur, article, commande) a sa propre URL. Vous récupérez des données via GET, vous en créez via POST, vous les mettez à jour via PUT/PATCH. Tout est simple et prévisible. Mais souvent, vous recevez plus de données que nécessaire, ou vous devez effectuer plusieurs requêtes pour rassembler des entités liées.
GraphQL est un langage de requête et un environnement d'exécution. Le client décide lui-même des champs dont il a besoin. Une seule requête — et vous obtenez exactement ce que vous avez demandé. Mais cette flexibilité a un coût : la complexité de l'implémentation côté serveur et des problèmes potentiels de performance.
En Rust, les deux approches sont implémentées au plus haut niveau grâce au système de types et à l'asynchronisme. Mais creusons plus profondément.
REST en Rust : le classique avec actix-web
Commençons par le REST familier. Prenons l'exemple d'un blog simple : des utilisateurs, des articles et des commentaires. En REST, nous devrons créer plusieurs endpoints.
use actix_web::{web, App, HttpServer, Responder, HttpResponse};use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]struct Article { id: i32, title: String, content: String, author_id: i32,}
async fn get_article(path: web::Path<i32>) -> impl Responder { let article_id = path.into_inner(); // Imaginons que nous récupérons l'article depuis la BDD let article = Article { id: article_id, title: "GraphQL vs REST".to_string(), content: "Comparaison détaillée...".to_string(), author_id: 1, }; HttpResponse::Ok().json(article)}
async fn get_articles_by_author(path: web::Path<i32>) -> impl Responder { let author_id = path.into_inner(); // Liste des articles de l'auteur let articles = vec![ Article { id: 1, title: "Article 1".to_string(), content: "Texte...".to_string(), author_id }, Article { id: 2, title: "Article 2".to_string(), content: "Texte...".to_string(), author_id }, ]; HttpResponse::Ok().json(articles)}
#[actix_web::main]async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .route("/articles/{id}", web::get().to(get_article)) .route("/authors/{id}/articles", web::get().to(get_articles_by_author)) }) .bind("127.0.0.1:8080")? .run() .await}Tout semble propre et clair. Avantages de REST en Rust :
- Simplicité du cache au niveau HTTP (ETag, Cache-Control).
- Structure transparente — chaque endpoint fait une seule chose.
- Intégration facile avec n'importe quel client (curl, Postman, navigateur).
Mais il y a aussi des inconvénients :
- Problème des requêtes N+1. Pour obtenir un article et son auteur, il faut deux requêtes. Ou créer un endpoint séparé avec un JOIN.
- Sur-récupération (over-fetching).<