Введение: почему Rust и GraphQL — идеальная пара
Современная веб-разработка требует высокой производительности и строгой типизации. Rust, известный своей безопасностью памяти и скоростью, становится отличным выбором для создания бэкендов. GraphQL, в свою очередь, предоставляет гибкий и эффективный способ работы с данными. В этом туториале мы создадим полноценный GraphQL API на Rust, используя библиотеку async-graphql и фреймворк actix-web. Вы научитесь определять схемы, писать резолверы и обрабатывать запросы.
К концу статьи у вас будет рабочий сервер, готовый к интеграции в реальные проекты. Все примеры кода проверены и готовы к запуску.
Настройка проекта и установка зависимостей
Для начала создадим новый проект Rust. Убедитесь, что у вас установлен Rust (рекомендуется версия 1.75+).
cargo new graphql-rust-api
cd graphql-rust-api
Откройте Cargo.toml и добавьте следующие зависимости:
[dependencies]
actix-web = "4"
actix-rt = "2"
async-graphql = "7"
async-graphql-actix-web = "7"
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["macros"] }
Запустите cargo build, чтобы загрузить и скомпилировать все зависимости. Это может занять несколько минут при первом запуске.
Структура проекта
Создайте следующую структуру папок и файлов:
src/
├── main.rs
├── schema.rs
├── models.rs
└── resolvers.rs
Мы будем постепенно заполнять каждый файл.
Определение моделей и схемы GraphQL
Начнём с создания простой модели данных. В models.rs опишем структуру User:
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: u32,
pub name: String,
pub email: String,
}
Теперь перейдём к schema.rs. Здесь мы определим GraphQL-объекты и запросы:
use async_graphql::{Context, Object, SimpleObject};
use crate::models::User;
use crate::resolvers::get_all_users;
#[derive(SimpleObject)]
pub struct UserObject {
pub id: u32,
pub name: String,
pub email: String,
}
pub struct QueryRoot;
#[Object]
impl QueryRoot {
async fn users(&self, ctx: &Context<'_>) -> Vec<UserObject> {
let users = get_all_users(ctx).await;
users.into_iter().map(|u| UserObject {
id: u.id,
name: u.name,
email: u.email,
}).collect()
}
}
Обратите внимание на использование #[derive(SimpleObject)] — это макрос async-graphql, который автоматически генерирует GraphQL-тип.
Резолверы: бизнес-логика
В файле resolvers.rs реализуем функцию получения данных. Для простоты будем использовать статический список:
use async_graphql::Context;
use crate::models::User;
pub async fn get_all_users(_ctx: &Context<'_>) -> Vec<User> {
vec![
User { id: 1, name: "Alice".into(), email: "alice@example.com".into() },
User { id: 2, name: "Bob".into(), email: "bob@example.com".into() },
User { id: 3, name: "Charlie".into(), email: "charlie@example.com".into() },
]
}
В реальном проекте здесь может быть обращение к базе данных или внешнему API.
Сборка сервера на Actix-web
Теперь самое главное — собрать всё вместе в main.rs:
use actix_web::{web, App, HttpServer, HttpResponse};
use async_graphql::{Schema, EmptyMutation, EmptySubscription};
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
use crate::schema::QueryRoot;
mod models;
mod schema;
mod resolvers;
type AppSchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;
async fn graphql_handler(
schema: web::Data<AppSchema>,
req: GraphQLRequest,
) -> GraphQLResponse {
schema.execute(req.into_inner()).await.into()
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
.finish();
println!("Сервер запущен на http://127.0.0.1:8080");
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(schema.clone()))
.route("/graphql", web::post().to(graphql_handler))
.route("/graphiql", web::get().to(|| async {
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(include_str!("../static/graphiql.html"))
}))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Для удобства тестирования добавим простую HTML-страницу GraphiQL. Создайте папку static в корне проекта и файл graphiql.html:
<!DOCTYPE html>
<html>
<head>
<title>GraphiQL</title>
<link href="https://unpkg.com/graphiql/graphiql.min.css" rel="stylesheet" />
</head>
<body style="margin: 0;">
<div id="graphiql">Loading...</div>
<script src="https://unpkg.com/graphiql/graphiql.min.js"></script>
<script>
const fetcher = graphQLFetcher('http://127.0.0.1:8080/graphql');
ReactDOM.render(
React.createElement(GraphiQL, { fetcher: fetcher }),
document.getElementById('graphiql')
);
</script>
</body>
</html>
Запуск и тестирование
Выполните команду cargo run и откройте браузер по адресу http://127.0.0.1:8080/graphiql. Введите следующий запрос:
{
users {
id
name
email
}
}
Вы должны получить JSON-ответ со списком пользователей. Поздравляю, ваш первый GraphQL API на Rust работает!