GraphQL em Rust
Exponha consultas flexíveis com async-graphql e Axum - schemas, resolvers e DataLoader para prevenção de N+1.
Receita
Cartão de referência rápida - pronto para copiar e colar.
use async_graphql::{Context, Object, Schema, SimpleObject};
#[derive(SimpleObject)]
struct User { id: i64, name: String }
struct Query;
#[Object]
impl Query {
async fn user(&self, ctx: &Context<'_>, id: i64) -> async_graphql::Result<User> {
Ok(User { id, name: "Ada".into() })
}
}
type AppSchema = Schema<Query, async_graphql::EmptyMutation, async_graphql::EmptySubscription>;Quando usar isso: Clientes precisam de seleção flexível de campos e grafos aninhados - aplicativos móveis, BFFs ou exploradores de administração.
Exemplo Funcional
use async_graphql::{Context, Object, Schema, SimpleObject};
use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
use axum::{routing::post, Router};
#[derive(SimpleObject, Clone)]
struct User { id: i64, name: String }
#[derive(Clone)]
struct AppState { users: Vec<User> }
struct Query;
#[Object]
impl Query {
async fn users(&self, ctx: &Context<'_>) -> Vec<User> {
ctx.data_unchecked::<AppState>().users.clone()
}
}
type AppSchema = Schema<Query, async_graphql::EmptyMutation, async_graphql::EmptySubscription>;
async fn graphql_handler(
schema: axum::Extension<AppSchema>,
req: GraphQLRequest,
) -> GraphQLResponse {
schema.0.execute(req.into_inner()).await.into()
}
#[tokio::main]
async fn main() {
let state = AppState { users: vec![User { id: 1, name: "Ada".into() }] };
let schema = Schema::build(Query, async_graphql::EmptyMutation, async_graphql::EmptySubscription)
.data(state)
.finish();
let app = Router::new()
.route("/graphql", post(graphql_handler))
.layer(axum::Extension(schema));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}O que isso demonstra:
#[Object]impl define os resolvers raiz da Query.SimpleObjectpara tipos de saída.- Schema construído com
.data(state)para contexto do resolver. async_graphql_axumintegra-se ao manipulador POST do Axum.
Mergulho Profundo
Como Funciona
- Ponto de extremidade
/graphqlúnico aceita documento de consulta + JSON de variáveis. - Resolvers buscam campos de forma assíncrona; o executor agrupa onde o DataLoader está configurado.
- Introspecção habilita o playground GraphiQL em desenvolvimento.
GraphQL vs REST
| Tópico | GraphQL | REST |
|---|---|---|
| Over-fetching | Cliente escolhe campos | DTO de resposta fixo |
| Versionamento | Evoluir schema cuidadosamente | URL /v1 |
| Caching | Mais difícil na camada HTTP | GET amigável para cache |
| Complexidade | Limites de profundidade/custo de consulta necessários | Operações mais simples |
Notas de Rust
// DataLoader para busca em lote no banco de dados
// use async_graphql::dataloader::LoaderArmadilhas
- N+1 sem DataLoader - Lista usuários e depois consulta pedidos para cada um. Correção: Loader por solicitação.
- Profundidade de consulta ilimitada - Ataque de negação de serviço (DoS) com consulta aninhada maliciosa. Correção:
limit_depthe análise de complexidade. - Introspecção em produção - Divulgação do schema. Correção: Desabilitar ou proteger o playground com autenticação.
- Autenticação em nível de campo - Verificações esquecidas em resolvers aninhados. Correção: Proteger em cada resolver ou extensões de schema.
- Vazamento de erros - Erros de banco de dados na matriz
errorsdo GraphQL. Correção: Mapear apenas para extensões seguras.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| REST + OpenAPI | CRUD público simples | Necessidades de campo de cliente altamente variáveis |
| gRPC | Interno de malha de serviço | Clientes de navegador |
juniper | Ecossistema Juniper mais antigo | Novos projetos - async-graphql é o padrão |
FAQs
Assinaturas?
Transporte WebSocket com raiz de assinatura async-graphql.
Uploads de arquivos?
Suporte à especificação multipart do GraphQL em async-graphql.
Auth?
Extrair token bearer no middleware Axum; inserir AuthUser nos dados do schema.
Consultas persistidas?
APQ para clientes móveis reduzirem o tamanho da carga útil.
Codegen?
async-graphql exporta o SDL do schema para codegen de frontend.
Limite de taxa?
Pontuação de complexidade da consulta * orçamento de custo por IP.
Testes?
schema.execute(Request::new(query)).await em testes unitários.
Coexistência REST?
Mesmo roteador Axum - rotas /graphql e /v1/*.
Integração sqlx?
Passar PgPool nos dados do schema; resolvers usam sqlx diretamente.
Quando pular GraphQL?
CRUD simples com poucos clientes - REST tem menos sobrecarga operacional.
Relacionados
- Noções Básicas de Design de API - Convenções REST
- Padrões de Consulta e N+1 - Racional do DataLoader
- Autenticação e Sessões - Padrões de autenticação
- Limite de Taxa e Prevenção de Abuso - Limites de custo de consulta
- Validação de Entrada e Injeção - Consultas não confiáveis
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.