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

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

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

Начать курс

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

GraphQL — это мощный язык запросов для API, который позволяет клиентам запрашивать только те данные, которые им нужны. В сочетании с производительностью и безопасностью Rust вы получаете идеальное решение для высоконагруженных веб-сервисов. В этом туториале мы создадим полноценное GraphQL API с нуля, используя крейт async-graphql.

Почему Rust? Он обеспечивает нулевую стоимость абстракций, безопасность памяти и потрясающую скорость. GraphQL на Rust работает быстрее, чем аналоги на JavaScript или Python, что критично для production-систем.

1. Настройка проекта и зависимости

Начнём с создания нового проекта Rust. Откройте терминал и выполните:

cargo new graphql_rust_api
cd graphql_rust_api

Теперь добавим необходимые зависимости в файл Cargo.toml:

[dependencies]
async-graphql = "7.0"
async-graphql-actix-web = "7.0"
actix-web = "4"
actix-cors = "0.7"
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }

Эти крейты предоставляют всё необходимое: async-graphql — ядро GraphQL, actix-web — веб-сервер, serde — сериализацию данных.

2. Создание модели данных и схемы

Определим простую модель для нашего API — книги. Создадим структуры и схему GraphQL:

use async_graphql::*;
use serde::{Serialize, Deserialize};

#[derive(Clone, Debug, Serialize, Deserialize, SimpleObject)]
struct Book {
    id: ID,
    title: String,
    author: String,
    year: i32,
}

#[derive(Clone, Debug, Serialize, Deserialize, InputObject)]
struct BookInput {
    title: String,
    author: String,
    year: i32,
}

Обратите внимание на атрибуты #[derive(SimpleObject)] и #[derive(InputObject)]. Первый автоматически генерирует GraphQL-тип для вывода, второй — для ввода данных.

3. Реализация резолверов (Query и Mutation)

Резолверы — это функции, которые обрабатывают запросы. Создадим структуру хранилища и резолверы:

use std::sync::Mutex;

struct QueryRoot;

#[Object]
impl QueryRoot {
    async fn books(&self, ctx: &Context<'_>) -> Vec<Book> {
        let storage = ctx.data_unchecked::<Mutex<Vec<Book>>>();
        storage.lock().unwrap().clone()
    }

    async fn book(&self, ctx: &Context<'_>, id: ID) -> Option<Book> {
        let storage = ctx.data_unchecked::<Mutex<Vec<Book>>>();
        storage.lock().unwrap().iter().find(|b| b.id == id).cloned()
    }
}

struct MutationRoot;

#[Object]
impl MutationRoot {
    async fn add_book(&self, ctx: &Context<'_>, input: BookInput) -> Book {
        let mut storage = ctx.data_unchecked::<Mutex<Vec<Book>>>()
            .lock().unwrap();
        let book = Book {
            id: ID::from(storage.len() + 1),
            title: input.title,
            author: input.author,
            year: input.year,
        };
        storage.push(book.clone());
        book
    }
}

Мы используем Mutex для потокобезопасного доступа к данным. В реальном проекте вы бы подключили базу данных, но для туториала это идеальный вариант.

4. Сборка и запуск сервера

Финальный шаг — объединить всё в работающий сервер. Создадим точку входа:

use actix_web::{guard, web, App, HttpServer, HttpRequest, HttpResponse};
use actix_cors::Cors;
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};

async fn index(
    schema: web::Data<Schema<QueryRoot, MutationRoot, EmptySubscription>>,
    req: GraphQLRequest,
) -> GraphQLResponse {
    schema.execute(req.into_inner()).await.into()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let storage = Mutex::new(vec![
        Book {
            id: ID::from(1),
            title: "Rust Programming".to_string(),
            author: "Steve Klabnik".to_string(),
            year: 2023,
        },
    ]);

    let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
        .data(storage)
        .finish();

    HttpServer::new(move || {
        let cors = Cors::default()
            .allow_any_origin()
            .allow_any_method()
            .allow_any_header();

        App::new()
            .wrap(cors)
            .data(schema.clone())
            .service(web::resource("/graphql")
                .route(web::post().to(index))
                .route(web::get().to(index)))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Запустите сервер командой cargo run и откройте браузер. GraphQL Playground будет доступен по адресу http://127.0.0.1:8080/graphql.

5. Тестирование API

Вот несколько примеров запросов, которые вы можете выполнить в Playground:

Запрос всех книг:

query {
  books {
    id
    title
    author
    year
  }
}

Добавление новой книги:

mutation {
  addBook(input: { title: "Learn Rust", author: "John Doe", year: 2024 }) {
    id
    title
  }
}

Запрос конкретной книги:

query {
  book(id: "1") {
    title
    author
  }
}

Заключение

Мы создали полноценное GraphQL API на Rust за несколько шагов. Этот туториал демонстрирует ключевые преимущества Rust: безопасность типов на уровне компиляции, отличную производительность и простоту интеграции с современными веб-фреймворками.

Что дальше? Вы можете расширить API, добавив:

  • Подключение PostgreSQL через sqlx или diesel
  • Аутентификацию и авторизацию через JWT
  • Подписки (Subscriptions) для real-time обновлений
  • Пагинацию и фильтрацию запросов

Rust с async-graphql

Блоги

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