Juniper — GraphQL библиотека для Rust
Juniper — это быстрая, безопасная и эргономичная библиотека на языке Rust для построения GraphQL серверов. Она позволяет разрабатывать API с строгой типизацией, автоматической генерацией документации и поддержкой асинхронных запросов. Juniper идеально подходит для проектов, где критичны производительность, надежность и выразительность типов.
В отличие от традиционных REST API, GraphQL позволяет клиенту запрашивать только необходимые данные, что снижает нагрузку на сеть и сервер. Juniper реализует полную спецификацию GraphQL (включая подписки через WebSocket), что делает ее отличным выбором для современных веб-приложений на Rust.
Основные возможности
- Полная поддержка GraphQL — запросы, мутации, подписки.
- Асинхронность — из коробки работает с async/await (через futures).
- Интеграция с популярными фреймворками — Actix-web, Rocket, Warp, Tide, Axum.
- Автоматическая генерация схемы — на основе структур Rust.
- Поддержка контекста — передача состояния (БД, аутентификация) в резолверы.
- Расширяемость — кастомные скаляры, директивы, интерфейсы.
- Безопасность — строгая типизация на уровне компиляции, предотвращает ошибки времени выполнения.
- Документация — автоматическая генерация GraphQL Schema (SDL).
Установка
Добавьте juniper в Cargo.toml вашего проекта:
[dependencies]
juniper = "0.16"
Для интеграции с веб-фреймворком добавьте соответствующий крейт. Например, для Actix-web:
[dependencies]
juniper = "0.16"
actix-web = "4"
juniper_actix = "0.3"
Создание простого GraphQL сервера
Рассмотрим пример создания API для получения списка пользователей. Сначала определим модель данных и резолверы.
1. Определение типов GraphQL
Используем макросы #[derive(GraphQLObject)] и #[derive(GraphQLInputObject)]:
use juniper::{GraphQLObject, GraphQLInputObject};
#[derive(GraphQLObject)]
#[graphql(description = "Пользователь системы")]
struct User {
id: i32,
name: String,
email: String,
}
#[derive(GraphQLInputObject)]
#[graphql(description = "Новый пользователь")]
struct NewUser {
name: String,
email: String,
}
2. Создание контекста и корневых запросов
Контекст обычно содержит доступ к базе данных или другим сервисам:
use std::sync::Mutex;
struct Database {
users: Mutex<Vec<User>>,
}
impl Database {
fn new() -> Self {
Database {
users: Mutex::new(vec![
User { id: 1, name: "Alice".into(), email: "alice@example.com".into() },
User { id: 2, name: "Bob".into(), email: "bob@example.com".into() },
]),
}
}
}
// Контекст для резолверов
pub struct Context {
pub db: Database,
}
// Реализация типажа Context для Juniper
impl juniper::Context for Context {}
3. Определение корневого запроса (Query) и мутации (Mutation)
pub struct QueryRoot;
#[juniper::graphql_object(Context = Context)]
impl QueryRoot {
fn users(context: &Context) -> Vec<User> {
let db = context.db.users.lock().unwrap();
db.clone()
}
fn user(context: &Context, id: i32) -> Option<User> {
let db = context.db.users.lock().unwrap();
db.iter().find(|u| u.id == id).cloned()
}
}
pub struct MutationRoot;
#[juniper::graphql_object(Context = Context)]
impl MutationRoot {
fn create_user(context: &Context, new_user: NewUser) -> User {
let mut db = context.db.users.lock().unwrap();
let id = db.len() as i32 + 1;
let user = User {
id,
name: new_user.name,
email: new_user.email,
};
db.push(user.clone());
user
}
}
4. Создание схемы и запуск сервера (Actix-web)
use actix_web::{web, App, HttpServer, HttpResponse, middleware};
use juniper::http::graphiql::graphiql_source;
use juniper::http::GraphQLRequest;
use std::sync::Arc;
// Создаем схему
type Schema = juniper::RootNode<'static, QueryRoot, MutationRoot>