Introducción: por qué Rust y GraphQL son la pareja ideal
\nEl desarrollo web moderno exige alto rendimiento y tipado estricto. Rust, conocido por su seguridad de memoria y velocidad, se convierte en una excelente opción para crear backends. GraphQL, por su parte, proporciona una forma flexible y eficiente de trabajar con datos. En este tutorial crearemos una API GraphQL completa en Rust, utilizando la biblioteca async-graphql y el framework actix-web. Aprenderá a definir esquemas, escribir resolutores y manejar consultas.
Al final del artículo tendrá un servidor funcional, listo para integrarse en proyectos reales. Todos los ejemplos de código están probados y listos para ejecutarse.
\n\nConfiguración del proyecto e instalación de dependencias
\nPara empezar, crearemos un nuevo proyecto Rust. Asegúrese de tener Rust instalado (se recomienda la versión 1.75+).
\ncargo new graphql-rust-api\ncd graphql-rust-api\nAbra Cargo.toml y agregue las siguientes dependencias:
[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"] }\nEjecute cargo build para descargar y compilar todas las dependencias. Esto puede tomar unos minutos en la primera ejecución.
Estructura del proyecto
\nCree la siguiente estructura de carpetas y archivos:
\nsrc/\n├── main.rs\n├── schema.rs\n├── models.rs\n└── resolvers.rs\nIremos completando cada archivo gradualmente.
\n\nDefinición de modelos y esquema GraphQL
\nEmpecemos creando un modelo de datos simple. En models.rs describiremos la estructura 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}\nAhora pasemos a schema.rs. Aquí definiremos los objetos y consultas de GraphQL:
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 el uso de #[derive(SimpleObject)] — es una macro de async-graphql que genera automáticamente el tipo GraphQL.
Resolutores: lógica de negocio
\nEn el archivo resolvers.rs implementaremos la función de obtención de datos. Para simplificar, usaremos una 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}\nEn un proyecto real, aquí podría haber una consulta a la base de datos o a una API externa.
\n\nMontaje del servidor con Actix-web
\nAhora lo más importante: unir todo en 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