---
title: "GraphQL Rust: Guia Completo com Axum 2026 | Rust Brasil"
url: "https://rustlang.com.br/artigos/rust-para-graphql/"
markdown_url: "https://rustlang.com.br/artigos/rust-para-graphql.MD"
description: "GraphQL em Rust: async-graphql, Juniper, subscriptions e DataLoader com Axum. Guia prático completo com exemplos."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# GraphQL Rust: Guia Completo com Axum 2026 | Rust Brasil

GraphQL em Rust: async-graphql, Juniper, subscriptions e DataLoader com Axum. Guia prático completo com exemplos.


## Introdução

GraphQL se consolidou como uma alternativa poderosa ao REST para APIs modernas, e Rust oferece implementações que são ao mesmo tempo **type-safe** e **extremamente performáticas**. Enquanto servidores GraphQL em Node.js ou Python dependem de resolução dinâmica e introspecção em runtime, as bibliotecas Rust aproveitam o sistema de tipos da linguagem para **validar o schema inteiro em tempo de compilação**, eliminando uma classe inteira de erros que só apareceriam em produção com outras linguagens.

As duas principais bibliotecas são **async-graphql** (mais popular, abordagem code-first com macros) e **Juniper** (mais antiga, com suporte a schema-first). Neste artigo, vamos focar em async-graphql por sua ergonomia, performance e recursos avançados como **subscriptions**, **DataLoader** e integração nativa com **Axum**. Se você ainda está escolhendo seu framework web, veja nossa [comparação entre Axum e Actix Web](/artigos/axum-vs-actix/).

## Async-graphql vs Juniper

| Aspecto | async-graphql | Juniper |
|---|---|---|
| **Abordagem** | Code-first (macros derive) | Code-first + schema-first |
| **Async** | Nativo (tokio) | Suportado |
| **Subscriptions** | WebSocket nativo | Suporte limitado |
| **DataLoader** | Integrado | Manual |
| **Integração web** | Axum, Actix, Rocket, Warp | Axum, Actix, Rocket, Warp |
| **Popularidade** | Mais popular (mais downloads) | Mais antigo, estável |
| **Documentação** | Muito boa | Boa |

## Configuração do Projeto

```toml
[package]
name = "api-graphql"
version = "0.1.0"
edition = "2021"

[dependencies]
async-graphql = { version = "7", features = ["dataloader"] }
async-graphql-axum = "7"
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "uuid", "chrono"] }
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
anyhow = "1"
tracing = "0.1"
tracing-subscriber = "0.3"
```

## Exemplo Prático: API GraphQL para Blog

Vamos construir uma API completa com queries, mutations, subscriptions e DataLoader para um sistema de blog.

### Definição dos Tipos

```rust
use async_graphql::*;
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// Representa um autor do blog.
#[derive(Debug, Clone, Serialize, Deserialize, SimpleObject)]
#[graphql(complex)]
pub struct Autor {
    pub id: Uuid,
    pub nome: String,
    pub email: String,
    pub bio: Option<String>,
    pub criado_em: NaiveDateTime,
}

/// Campos computados do Autor (resolvidos via DataLoader).
#[ComplexObject]
impl Autor {
    /// Retorna todos os posts deste autor.
    async fn posts(&self, ctx: &Context<'_>) -> Result<Vec<Post>> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let posts = sqlx::query_as!(
            Post,
            r#"SELECT id, titulo, conteudo, publicado,
                      autor_id, criado_em, atualizado_em
               FROM posts WHERE autor_id = $1
               ORDER BY criado_em DESC"#,
            self.id
        )
        .fetch_all(db)
        .await?;
        Ok(posts)
    }

    /// Total de posts do autor.
    async fn total_posts(&self, ctx: &Context<'_>) -> Result<i64> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let resultado = sqlx::query_scalar!(
            "SELECT COUNT(*) FROM posts WHERE autor_id = $1",
            self.id
        )
        .fetch_one(db)
        .await?;
        Ok(resultado.unwrap_or(0))
    }
}

/// Representa um post do blog.
#[derive(Debug, Clone, Serialize, Deserialize, SimpleObject)]
#[graphql(complex)]
pub struct Post {
    pub id: Uuid,
    pub titulo: String,
    pub conteudo: String,
    pub publicado: bool,
    pub autor_id: Uuid,
    pub criado_em: NaiveDateTime,
    pub atualizado_em: NaiveDateTime,
}

#[ComplexObject]
impl Post {
    /// Retorna o autor deste post (usando DataLoader para evitar N+1).
    async fn autor(&self, ctx: &Context<'_>) -> Result<Option<Autor>> {
        let loader = ctx.data::<DataLoader<AutorLoader>>()?;
        let autor = loader.load_one(self.autor_id).await?;
        Ok(autor)
    }

    /// Retorna os comentários deste post.
    async fn comentarios(&self, ctx: &Context<'_>) -> Result<Vec<Comentario>> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let comentarios = sqlx::query_as!(
            Comentario,
            r#"SELECT id, conteudo, autor_nome, post_id, criado_em
               FROM comentarios WHERE post_id = $1
               ORDER BY criado_em ASC"#,
            self.id
        )
        .fetch_all(db)
        .await?;
        Ok(comentarios)
    }
}

/// Comentário em um post.
#[derive(Debug, Clone, Serialize, Deserialize, SimpleObject)]
pub struct Comentario {
    pub id: Uuid,
    pub conteudo: String,
    pub autor_nome: String,
    pub post_id: Uuid,
    pub criado_em: NaiveDateTime,
}
```

### DataLoader para Evitar N+1

```rust
use async_graphql::dataloader::Loader;
use std::collections::HashMap;

pub struct AutorLoader {
    pub pool: sqlx::PgPool,
}

impl Loader<Uuid> for AutorLoader {
    type Value = Autor;
    type Error = Arc<sqlx::Error>;

    async fn load(
        &self,
        keys: &[Uuid],
    ) -> Result<HashMap<Uuid, Self::Value>, Self::Error> {
        let autores = sqlx::query_as!(
            Autor,
            r#"SELECT id, nome, email, bio, criado_em
               FROM autores WHERE id = ANY($1)"#,
            keys
        )
        .fetch_all(&self.pool)
        .await
        .map_err(Arc::new)?;

        Ok(autores.into_iter().map(|a| (a.id, a)).collect())
    }
}

use std::sync::Arc;
```

### Queries e Mutations

```rust
use async_graphql::*;
use uuid::Uuid;

pub struct QueryRoot;

#[Object]
impl QueryRoot {
    /// Busca todos os posts publicados.
    async fn posts(
        &self,
        ctx: &Context<'_>,
        #[graphql(default = 20)] limite: i64,
        #[graphql(default = 0)] offset: i64,
    ) -> Result<Vec<Post>> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let posts = sqlx::query_as!(
            Post,
            r#"SELECT id, titulo, conteudo, publicado,
                      autor_id, criado_em, atualizado_em
               FROM posts WHERE publicado = true
               ORDER BY criado_em DESC
               LIMIT $1 OFFSET $2"#,
            limite,
            offset
        )
        .fetch_all(db)
        .await?;
        Ok(posts)
    }

    /// Busca um post pelo ID.
    async fn post(&self, ctx: &Context<'_>, id: Uuid) -> Result<Option<Post>> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let post = sqlx::query_as!(
            Post,
            r#"SELECT id, titulo, conteudo, publicado,
                      autor_id, criado_em, atualizado_em
               FROM posts WHERE id = $1"#,
            id
        )
        .fetch_optional(db)
        .await?;
        Ok(post)
    }

    /// Busca posts por texto (título ou conteúdo).
    async fn buscar_posts(
        &self,
        ctx: &Context<'_>,
        termo: String,
    ) -> Result<Vec<Post>> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let termo_busca = format!("%{}%", termo);
        let posts = sqlx::query_as!(
            Post,
            r#"SELECT id, titulo, conteudo, publicado,
                      autor_id, criado_em, atualizado_em
               FROM posts
               WHERE publicado = true
                 AND (titulo ILIKE $1 OR conteudo ILIKE $1)
               ORDER BY criado_em DESC"#,
            termo_busca
        )
        .fetch_all(db)
        .await?;
        Ok(posts)
    }

    /// Retorna todos os autores.
    async fn autores(&self, ctx: &Context<'_>) -> Result<Vec<Autor>> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let autores = sqlx::query_as!(
            Autor,
            "SELECT id, nome, email, bio, criado_em FROM autores ORDER BY nome"
        )
        .fetch_all(db)
        .await?;
        Ok(autores)
    }
}

// === Inputs ===

#[derive(InputObject)]
pub struct NovoPostInput {
    pub titulo: String,
    pub conteudo: String,
    pub autor_id: Uuid,
    #[graphql(default = false)]
    pub publicado: bool,
}

#[derive(InputObject)]
pub struct AtualizarPostInput {
    pub titulo: Option<String>,
    pub conteudo: Option<String>,
    pub publicado: Option<bool>,
}

#[derive(InputObject)]
pub struct NovoComentarioInput {
    pub conteudo: String,
    pub autor_nome: String,
    pub post_id: Uuid,
}

// === Mutations ===

pub struct MutationRoot;

#[Object]
impl MutationRoot {
    /// Cria um novo post.
    async fn criar_post(
        &self,
        ctx: &Context<'_>,
        input: NovoPostInput,
    ) -> Result<Post> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let id = Uuid::new_v4();
        let agora = chrono::Utc::now().naive_utc();

        let post = sqlx::query_as!(
            Post,
            r#"INSERT INTO posts (id, titulo, conteudo, publicado, autor_id, criado_em, atualizado_em)
               VALUES ($1, $2, $3, $4, $5, $6, $6)
               RETURNING id, titulo, conteudo, publicado, autor_id, criado_em, atualizado_em"#,
            id,
            input.titulo,
            input.conteudo,
            input.publicado,
            input.autor_id,
            agora
        )
        .fetch_one(db)
        .await?;

        Ok(post)
    }

    /// Atualiza um post existente.
    async fn atualizar_post(
        &self,
        ctx: &Context<'_>,
        id: Uuid,
        input: AtualizarPostInput,
    ) -> Result<Post> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let agora = chrono::Utc::now().naive_utc();

        let post = sqlx::query_as!(
            Post,
            r#"UPDATE posts SET
                titulo = COALESCE($2, titulo),
                conteudo = COALESCE($3, conteudo),
                publicado = COALESCE($4, publicado),
                atualizado_em = $5
               WHERE id = $1
               RETURNING id, titulo, conteudo, publicado, autor_id, criado_em, atualizado_em"#,
            id,
            input.titulo,
            input.conteudo,
            input.publicado,
            agora
        )
        .fetch_one(db)
        .await?;

        Ok(post)
    }

    /// Adiciona um comentário a um post.
    async fn comentar(
        &self,
        ctx: &Context<'_>,
        input: NovoComentarioInput,
    ) -> Result<Comentario> {
        let db = ctx.data::<sqlx::PgPool>()?;
        let id = Uuid::new_v4();
        let agora = chrono::Utc::now().naive_utc();

        let comentario = sqlx::query_as!(
            Comentario,
            r#"INSERT INTO comentarios (id, conteudo, autor_nome, post_id, criado_em)
               VALUES ($1, $2, $3, $4, $5)
               RETURNING id, conteudo, autor_nome, post_id, criado_em"#,
            id,
            input.conteudo,
            input.autor_nome,
            input.post_id,
            agora
        )
        .fetch_one(db)
        .await?;

        Ok(comentario)
    }
}
```

### Servidor com Axum e Playground

```rust
use async_graphql::{dataloader::DataLoader, EmptySubscription, Schema};
use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
use axum::{
    extract::State,
    routing::get,
    Router,
};

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

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

async fn graphql_playground() -> impl axum::response::IntoResponse {
    axum::response::Html(
        async_graphql::http::playground_source(
            async_graphql::http::GraphQLPlaygroundConfig::new("/graphql")
        )
    )
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt::init();

    // Conectar ao banco de dados
    let pool = sqlx::PgPool::connect("postgres://user:pass@localhost/blog")
        .await?;

    // Criar DataLoader
    let autor_loader = DataLoader::new(
        AutorLoader { pool: pool.clone() },
        tokio::spawn,
    );

    // Criar schema GraphQL
    let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
        .data(pool)
        .data(autor_loader)
        .finish();

    let app = Router::new()
        .route("/graphql", get(graphql_playground).post(graphql_handler))
        .with_state(schema);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
    tracing::info!("GraphQL playground em http://localhost:8080/graphql");
    axum::serve(listener, app).await?;

    Ok(())
}
```

### Exemplo de Queries GraphQL

```graphql
# Buscar posts com autores
query {
  posts(limite: 10) {
    id
    titulo
    conteudo
    publicado
    criadoEm
    autor {
      nome
      email
      totalPosts
    }
    comentarios {
      conteudo
      autorNome
    }
  }
}

# Criar novo post
mutation {
  criarPost(input: {
    titulo: "Introdução ao Rust"
    conteudo: "Rust é uma linguagem..."
    autorId: "550e8400-e29b-41d4-a716-446655440000"
    publicado: true
  }) {
    id
    titulo
  }
}

# Buscar posts por texto
query {
  buscarPosts(termo: "rust") {
    titulo
    autor {
      nome
    }
  }
}
```

## Performance: GraphQL em Rust vs Outras Linguagens

| Métrica | Rust (async-graphql) | Node.js (Apollo) | Python (Strawberry) |
|---|---|---|---|
| Req/s (query simples) | ~45.000 | ~8.000 | ~2.500 |
| Req/s (query complexa) | ~12.000 | ~2.000 | ~500 |
| Latência p99 | 2ms | 15ms | 45ms |
| Memória em idle | 5 MB | 80 MB | 50 MB |
| Memória sob carga | 25 MB | 300 MB | 200 MB |

## Empresas Usando GraphQL com Rust

- **Grafbase**: Plataforma GraphQL serverless construída inteiramente em Rust
- **Apollo (parcial)**: Router do Apollo Federation tem componentes em Rust
- **Cloudflare**: APIs internas GraphQL de alta performance
- **Shopify**: Componentes de performance para a API GraphQL
- **GitHub**: Experimenta Rust para componentes de alta performance na API GraphQL

## Como Começar

1. **Aprenda Rust**: [Tutorial de primeiros passos](/tutoriais/primeiros-passos/) — entenda ownership antes de criar APIs
2. **Domine REST primeiro**: Siga o [tutorial de API REST com Axum](/tutoriais/api-rest-axum/) para entender o ecossistema web
3. **Async Rust**: GraphQL é assíncrono por natureza — [receita de async/await](/receitas/async-await-basico/)
4. **Banco de dados**: Conecte ao [PostgreSQL](/tutoriais/rust-postgresql/) com SQLx
5. **JSON**: Entenda [serialização JSON](/receitas/serializar-json/) com serde
6. **Comece com async-graphql**: A documentação oficial e exemplos são excelentes

## Conclusão

Rust combina perfeitamente com GraphQL. A validação de schema em tempo de compilação, a resolução assíncrona eficiente e o DataLoader integrado tornam async-graphql uma das melhores implementações GraphQL em qualquer linguagem. Se você já tem uma API REST em Rust e quer migrar para GraphQL, ou está começando um projeto novo que precisa de flexibilidade nas queries, Rust com async-graphql é uma escolha excelente.

---

## Veja Também

- [Tutorial: API REST com Axum](/tutoriais/api-rest-axum/) — Comece com REST antes de migrar para GraphQL
- [Rust para Desenvolvimento Web](/artigos/rust-para-web/) — Ecossistema web completo
- [Rust para Microsserviços](/artigos/rust-para-microsservicos/) — Combine GraphQL com gRPC
- [Tutorial: Rust + PostgreSQL](/tutoriais/rust-postgresql/) — Banco de dados para sua API
- [Receita: Serializar JSON](/receitas/serializar-json/) — Essencial para APIs
- [Receita: Async/Await Básico](/receitas/async-await-basico/) — Fundamento para GraphQL assíncrono

---

Se você trabalha com GraphQL em outras linguagens, veja também:

- <a href="https://kotlin.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">Kotlin GraphQL: APIs type-safe com GraphQL Kotlin</a>
- <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go GraphQL com gqlgen: geração de código e schemas type-safe em Go</a>
