Introdução: por que Rust e GraphQL são a combinação ideal
\nO desenvolvimento web moderno exige alto desempenho e tipagem estrita. Rust, conhecido por sua segurança de memória e velocidade, torna-se uma excelente escolha para criar backends. GraphQL, por sua vez, oferece uma forma flexível e eficiente de trabalhar com dados. Neste tutorial, criaremos uma GraphQL API completa em Rust, usando a biblioteca async-graphql e o framework actix-web. Você aprenderá a definir esquemas, escrever resolvers e processar requisições.
Ao final do artigo, você terá um servidor funcional, pronto para integração em projetos reais. Todos os exemplos de código foram testados e estão prontos para execução.
\n\nConfiguração do projeto e instalação de dependências
\nPara começar, vamos criar um novo projeto Rust. Certifique-se de ter o Rust instalado (versão recomendada: 1.75+).
\ncargo new graphql-rust-api\ncd graphql-rust-api\nAbra o arquivo Cargo.toml e adicione as seguintes dependências:
[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"] }\nExecute cargo build para baixar e compilar todas as dependências. Isso pode levar alguns minutos na primeira execução.
Estrutura do projeto
\nCrie a seguinte estrutura de pastas e arquivos:
\nsrc/\n├── main.rs\n├── schema.rs\n├── models.rs\n└── resolvers.rs\nVamos preencher cada arquivo gradualmente.
\n\nDefinição de modelos e esquema GraphQL
\nVamos começar criando um modelo de dados simples. Em models.rs, descrevemos a estrutura 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}\nAgora, vamos para schema.rs. Aqui, definimos os objetos GraphQL e as consultas:
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}\nObserve o uso de #[derive(SimpleObject)] — esta é uma macro do async-graphql que gera automaticamente o tipo GraphQL.
Resolvers: lógica de negócio
\nNo arquivo resolvers.rs, implementamos a função de obtenção de dados. Para simplificar, usaremos uma lista estática:
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}\nEm um projeto real, aqui poderia haver uma consulta ao banco de dados ou a uma API externa.
\n\nMontagem do servidor com Actix-web
\nAgora, o mais importante: juntar tudo no 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