GraphQL API с Rust: пошаговый туториал на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему 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 работает!

Заключение

Блоги

Рекомендаций по книгам