Por que Rust é o candidato ideal para trabalhar com API?
\n\nSe você ainda pensa que Rust é apenas para programação de sistemas e substituição do C++, está muito atrasado em relação à realidade. Rust invadiu com confiança o mundo do desenvolvimento web e dos serviços backend. E não apenas invadiu, mas ocupou um nicho onde são importantes velocidade, confiabilidade e segurança de memória.
\n\nMas sejamos honestos: trabalhar com API em Rust não é apenas "pegar uma biblioteca pronta e esquecer". É uma arte de equilíbrio entre o poder da linguagem e a facilidade de desenvolvimento. Neste artigo, vamos analisar três abordagens principais: REST, GraphQL e gRPC. Cada uma é boa à sua maneira, e a escolha depende da sua tarefa. Vamos nessa!
\n\nREST em Rust: o clássico que funciona
\n\nREST continua sendo o estilo de API mais popular. É simples, intuitivo e suportado por praticamente tudo. Para Rust, existem vários frameworks excelentes, mas o rei aqui é o actix-web. Ele é rápido, assíncrono e possui um ótimo ecossistema.
Escrevendo um serviço REST simples com actix-web
\n\nVamos criar um minisserviço para gerenciar uma lista de tarefas (todo). Precisaremos de actix-web, serde para serialização e tokio como runtime assíncrono.
// Cargo.toml\n// [dependencies]\n// actix-web = "4"\n// serde = { version = "1", features = ["derive"] }\n// serde_json = "1"\n// tokio = { version = "1", features = ["full"] }\n\nuse actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};\nuse serde::{Deserialize, Serialize};\nuse std::sync::Mutex;\n\n#[derive(Serialize, Deserialize, Clone)]\nstruct Task {\n id: u32,\n title: String,\n completed: bool,\n}\n\nstruct AppState {\n tasks: Mutex<Vec<Task>>,\n}\n\n#[get("/tasks")]\nasync fn get_tasks(data: web::Data<AppState>) -> impl Responder {\n let tasks = data.tasks.lock().unwrap();\n HttpResponse::Ok().json(tasks.clone())\n}\n\n#[post("/tasks")]\nasync fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> impl Responder {\n let mut tasks = data.tasks.lock().unwrap();\n let new_id = tasks.len() as u32 + 1;\n let task = Task {\n id: new_id,\n title: body.title.clone(),\n completed: false,\n };\n tasks.push(task.clone());\n HttpResponse::Created().json(task)\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n let state = web::Data::new(AppState {\n tasks: Mutex::new(vec![]),\n });\n\n HttpServer::new(move || {\n App::new()\n .app_data(state.clone())\n .service(get_tasks)\n .service(create_task)\n })\n .bind("127.0.0.1:8080")?\n .run()\n .await\n}\n\n\nA beleza deste código é que ele é seguro em termos de memória e não requer um coletor de lixo. O actix-web processa milhares de requisições por segundo em uma única thread, usando mecanismos assíncronos do Rust. Tente executá-lo — você ficará surpreso com o quão rápido ele é, mesmo em hardware modesto.
Quando o REST não é a melhor escolha?
\n\nO REST é excelente para operações CRUD, mas se você precisar consultar dados de forma flexível (por exemplo, recursos aninhados), o REST começa a exigir muitos endpoints. É aí que entra o GraphQL.
\n\nGraphQL em Rust: flexibilidade sem dor
\n\nO GraphQL permite que o cliente solicite exatamente os dados de que precisa. Para Rust, a melhor escolha é o async-graphql. Ele se integra perfeitamente com o actix-web e fornece uma DSL poderosa para descrever esquemas.
Exemplo: API para uma biblioteca de livros
\n\nSuponha que queremos dar aos clientes a capacidade de consultar livros e autores. Com GraphQL, isso é feito com um único esquema.
\n\n// Cargo.toml\n// [dependencies]\n// async-graphql = "7"\n// async-graphql-actix-web = "7"\n// actix-web = "4"\n// serde = { version = "1", features = ["derive"] }\n\nuse async_graphql::{Context, EmptyMutatio