GraphQL API с Rust: пошаговый туториал для начинающих

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

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

Начать курс

Введение в GraphQL API на Rust

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

Настройка проекта и установка зависимостей

Для начала создадим новый проект Rust с помощью Cargo:

cargo new graphql-rust-tutorial
cd graphql-rust-tutorial

Откройте файл Cargo.toml и добавьте следующие зависимости:

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

Поясним выбор библиотек:

  • async-graphql — основная библиотека для работы с GraphQL в Rust.
  • async-graphql-actix-web — интеграция с веб-фреймворком Actix-web.
  • actix-web — популярный асинхронный HTTP-сервер.
  • serde — для сериализации/десериализации данных.
  • tokio — асинхронный рантайм.

Выполните cargo build, чтобы убедиться, что все зависимости установлены корректно.

Определение схемы GraphQL и резолверов

Создадим простую модель данных — книгу. Определим структуру и резолверы для запросов и мутаций.

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

Создайте файл src/models.rs и добавьте следующий код:

use async_graphql::*;

#[derive(SimpleObject)]
pub struct Book {
    pub id: ID,
    pub title: String,
    pub author: String,
}

#[derive(InputObject)]
pub struct NewBook {
    pub title: String,
    pub author: String,
}

Здесь SimpleObject автоматически генерирует GraphQL-тип для структуры Book, а InputObject — для входных данных мутации.

Создание хранилища и резолверов

Создадим файл src/resolvers.rs, который будет содержать логику запросов и мутаций:

use std::sync::Mutex;
use async_graphql::*;
use crate::models::{Book, NewBook};

pub struct QueryRoot;

#[Object]
impl QueryRoot {
    async fn books(&self) -> Vec<Book> {
        let storage = STORAGE.lock().unwrap();
        storage.clone()
    }

    async fn book(&self, id: ID) -> Option<Book> {
        let storage = STORAGE.lock().unwrap();
        storage.iter().find(|b| b.id == id).cloned()
    }
}

pub struct MutationRoot;

#[Object]
impl MutationRoot {
    async fn add_book(&self, input: NewBook) -> Book {
        let mut storage = STORAGE.lock().unwrap();
        let new_book = Book {
            id: ID::from(format!("book_{}", storage.len() + 1)),
            title: input.title,
            author: input.author,
        };
        storage.push(new_book.clone());
        new_book
    }
}

lazy_static::lazy_static! {
    static ref STORAGE: Mutex<Vec<Book>> = Mutex::new(vec![]);
}

Обратите внимание: мы используем Mutex для потокобезопасного доступа к хранилищу. В реальном проекте лучше использовать базу данных.

Сборка схемы

Теперь объединим всё в файле src/main.rs:

use actix_web::{web, App, HttpServer, HttpResponse, middleware};
use async_graphql::{http::GraphiQLSource, EmptySubscription, Schema};
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
use std::sync::Arc;

mod models;
mod resolvers;

use resolvers::{QueryRoot, MutationRoot};

type AppSchema = Schema<QueryRoot, MutationRoot, EmptySubscription>;

async fn graphql_handler(schema: web::Data<AppSchema>, req: GraphQLRequest) -> GraphQLResponse {
    schema.execute(req.into_inner()).await.into()
}

async fn graphiql_handler() -> HttpResponse {
    HttpResponse::Ok()
        .content_type("text/html; charset=utf-8")
        .body(GraphiQLSource::build().endpoint("/graphql").finish())
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
        .data(Arc::new(()))
        .finish();

    println!("GraphQL API запущен на http://localhost:8000/graphql");

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(schema.clone()))
            .route("/graphql", web::post().to(graphql_handler))
            .route("/graphiql", web::get().to(graphiql_handler))
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

Здесь мы создаем HTTP-сервер на Actix-web, который обрабатывает POST-запросы к /graphql и предоставляет GraphiQL-интерфейс для тестирования по адресу /graphiql.

Запуск и тестирование API

Запустите сервер командой cargo run. Откройте браузер и перейдите по адресу http://localhost:8000/graphiql. Вы увидите интерфейс GraphiQL, где можно выполнять запросы.

Пример запроса (Query)

Добавьте несколько книг через мутацию, а затем выполните запрос:

query {
  books {
    id
    title
    author
  }
}

Ответ должен содержать список книг в формате JSON.

Пример мутации (Mutation)

mutation {
  addBook(input: { title: "Rust Programming", author: "Steve Klabnik" }) {
    id
    title
    author
  }
}

После выполнения мутации, новая книга появится в списке.

Заключение

В этом туториале мы создали полноценный GraphQL API на Rust с использованием async-graphql и actix-web. Вы узнали, как на

Блоги

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