Introdução: REST ou GraphQL — o debate eterno que vamos resolver em Rust
Se você programa em Rust e está escolhendo uma arquitetura para sua API, certamente já se perguntou: o que escolher — o clássico REST ou o moderno GraphQL? Ambas as abordagens têm exércitos de fãs e montanhas de detratores. O REST é acusado de redundância de dados e "conversa" excessiva entre cliente e servidor. O GraphQL, por sua vez, é criticado pela complexidade do cache e pelas consultas "infernais" ao banco de dados.
Mas vamos encarar a realidade: Rust não é JavaScript nem Python. Sua filosofia é baseada em zero-cost abstractions e desempenho máximo. Isso significa que uma das abordagens é mais adequada para o ecossistema Rust? Vamos descobrir com exemplos reais de código.
Neste artigo, não vamos apenas listar prós e contras. Vamos escrever dois microsserviços com actix-web (REST) e async-graphql, comparar seu desempenho, complexidade de desenvolvimento e facilidade de manutenção. Vamos nessa!
Resumo rápido: REST e GraphQL em 30 segundos
REST (Representational State Transfer) é um estilo arquitetural onde cada recurso (usuário, artigo, pedido) tem sua própria URL. Você obtém dados via GET, cria via POST, atualiza via PUT/PATCH. Tudo simples e previsível. Mas muitas vezes você recebe mais dados do que precisa, ou faz várias requisições para reunir entidades relacionadas.
GraphQL é uma linguagem de consulta e um ambiente de execução. O cliente decide quais campos deseja. Uma única consulta — e você obtém exatamente o que pediu. Mas essa flexibilidade tem um custo: maior complexidade de implementação no servidor e possíveis problemas de desempenho.
Em Rust, ambas as abordagens são implementadas no mais alto nível, graças ao sistema de tipos e à assincronicidade. Mas vamos nos aprofundar.
REST em Rust: o clássico com actix-web
Vamos começar com o REST tradicional. Como exemplo, usaremos um blog simples: com usuários, artigos e comentários. No REST, precisaremos criar vários 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(); // Vamos imaginar que buscamos o artigo do banco de dados let article = Article { id: article_id, title: "GraphQL vs REST".to_string(), content: "Comparação detalhada...".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(); // Lista de artigos do autor let articles = vec![ Article { id: 1, title: "Artigo 1".to_string(), content: "Texto...".to_string(), author_id }, Article { id: 2, title: "Artigo 2".to_string(), content: "Texto...".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}Tudo parece limpo e claro. Vantagens do REST em Rust:
- Simplicidade de cache no nível HTTP (ETag, Cache-Control).
- Estrutura transparente — cada endpoint faz uma coisa.
- Fácil integração com qualquer cliente (curl, Postman, navegador).
Mas também há desvantagens:
- Problema de consultas N+1. Para obter um artigo e o autor, são necessárias duas requisições. Ou criar um endpoint separado com JOIN.
- Over-fetching.<