Введение в 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. Вы узнали, как на