Juniper — Biblioteca GraphQL para Rust
Juniper é uma biblioteca rápida, segura e ergonômica na linguagem Rust para construir servidores GraphQL. Ela permite desenvolver APIs com tipagem estrita, geração automática de documentação e suporte a consultas assíncronas. Juniper é ideal para projetos onde desempenho, confiabilidade e expressividade de tipos são críticos.
Diferentemente das APIs REST tradicionais, o GraphQL permite que o cliente solicite apenas os dados necessários, reduzindo a carga na rede e no servidor. Juniper implementa a especificação completa do GraphQL (incluindo assinaturas via WebSocket), tornando-a uma excelente escolha para aplicações web modernas em Rust.
Principais recursos
- Suporte completo ao GraphQL — consultas, mutações, assinaturas.
- Assincronicidade — funciona nativamente com async/await (via futures).
- Integração com frameworks populares — Actix-web, Rocket, Warp, Tide, Axum.
- Geração automática de esquema — baseada em estruturas Rust.
- Suporte a contexto — passagem de estado (BD, autenticação) para os resolvers.
- Extensibilidade — escalares personalizados, diretivas, interfaces.
- Segurança — tipagem estrita em nível de compilação, previne erros em tempo de execução.
- Documentação — geração automática do GraphQL Schema (SDL).
Instalação
Adicione juniper ao Cargo.toml do seu projeto:
[dependencies]juniper = "0.16"Para integração com um framework web, adicione o crate correspondente. Por exemplo, para Actix-web:
[dependencies]juniper = "0.16"actix-web = "4"juniper_actix = "0.3"Criando um servidor GraphQL simples
Vamos considerar um exemplo de criação de uma API para obter uma lista de usuários. Primeiro, definimos o modelo de dados e os resolvers.
1. Definição dos tipos GraphQL
Usamos as macros #[derive(GraphQLObject)] e #[derive(GraphQLInputObject)]:
use juniper::{GraphQLObject, GraphQLInputObject};
#[derive(GraphQLObject)]#[graphql(description = "Usuário do sistema")]struct User { id: i32, name: String, email: String,}
#[derive(GraphQLInputObject)]#[graphql(description = "Novo usuário")]struct NewUser { name: String, email: String,}2. Criação do contexto e consultas raiz
O contexto geralmente contém acesso ao banco de dados ou outros serviços:
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() }, ]), } }}
// Contexto para os resolverspub struct Context { pub db: Database,}
// Implementação da trait Context para Juniperimpl juniper::Context for Context {}3. Definição da consulta raiz (Query) e mutação (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) -> U