Axum: Construindo APIs Web de Alta Performance com Rust em 2026

Aprenda a construir APIs web de alta performance com Axum em Rust. Guia completo com routing, extractors, middleware, SQLx e comparações com Actix-Web e Rocket.

Introdução

O Axum consolidou-se como o framework web mais popular do ecossistema Rust em 2026. Criado pela equipe do Tokio, ele combina a performance bruta do Rust com uma API ergonômica que facilita a construção de APIs REST, microserviços e aplicações web de produção. Diferente de outros frameworks que reinventam a roda, o Axum é construído sobre o Tokio e o Tower — o mesmo stack que alimenta serviços de infraestrutura crítica em empresas como AWS, Cloudflare e Discord.

Se você quer entender por que tantas empresas estão adotando Rust para seus backends — como discutimos em Rust em Produção — o Axum é o ponto de partida ideal. E se você vem do mundo Go, onde net/http e Gin dominam, vai notar semelhanças no modelo de handlers — Go também tem excelentes frameworks web, compare em Golang Brasil.

Filosofia de Design

O Axum segue uma filosofia minimalista e composicional. Em vez de criar abstrações próprias para middleware, extração de dados e gerenciamento de estado, ele aproveita o ecossistema Tower já existente. Isso significa que qualquer middleware escrito para Tower funciona automaticamente com Axum — e vice-versa.

Os principais diferenciais que impulsionaram sua adoção:

  • Sem macros mágicas: diferente do Rocket e Actix-Web, os handlers do Axum são funções async comuns
  • Tipagem forte nos extractors: erros de extração são detectados em tempo de compilação
  • Integração nativa com Tokio: sem overhead de adaptação entre runtimes
  • Ecossistema Tower: acesso a centenas de middlewares prontos para produção

Configuração Inicial e Routing Básico

Vamos começar com um projeto mínimo. Adicione as dependências no Cargo.toml:

// Cargo.toml
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Agora, o servidor básico com rotas:

use axum::{
    routing::{get, post},
    Router, Json,
};
use serde::{Deserialize, Serialize};

#[derive(Serialize)]
struct Mensagem {
    texto: String,
}

async fn raiz() -> Json<Mensagem> {
    Json(Mensagem {
        texto: "Bem-vindo à API Rust com Axum!".into(),
    })
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(raiz))
        .route("/usuarios", post(criar_usuario));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();

    println!("Servidor rodando em http://localhost:3000");
    axum::serve(listener, app).await.unwrap();
}

O modelo de routing do Axum é declarativo e intuitivo. Cada rota associa um caminho a um handler — uma função async que recebe extractors e retorna uma resposta.

Extractors: O Coração do Axum

Os extractors são o mecanismo que torna o Axum tão ergonômico. Eles extraem dados da requisição HTTP e os entregam tipados ao seu handler. Se a extração falhar, o Axum retorna automaticamente o erro HTTP apropriado.

Path, Query e Json

use axum::extract::{Path, Query, State};
use std::collections::HashMap;

// Extrai parâmetros da URL: /usuarios/42
async fn buscar_usuario(Path(id): Path<u64>) -> String {
    format!("Buscando usuário com ID: {id}")
}

// Extrai query params: /busca?termo=rust&pagina=1
async fn buscar(Query(params): Query<HashMap<String, String>>) -> String {
    let termo = params.get("termo").unwrap_or(&"".into());
    format!("Buscando por: {termo}")
}

// Extrai e valida JSON do body
#[derive(Deserialize)]
struct NovoUsuario {
    nome: String,
    email: String,
}

async fn criar_usuario(Json(dados): Json<NovoUsuario>) -> Json<Mensagem> {
    Json(Mensagem {
        texto: format!("Usuário {} criado com sucesso!", dados.nome),
    })
}

State: Compartilhando Estado entre Handlers

Para compartilhar conexões de banco de dados, configurações ou caches, o Axum usa o extractor State:

use axum::extract::State;
use std::sync::Arc;

struct AppState {
    db_url: String,
    nome_app: String,
}

#[tokio::main]
async fn main() {
    let estado = Arc::new(AppState {
        db_url: "postgres://localhost/meuapp".into(),
        nome_app: "Minha API Rust".into(),
    });

    let app = Router::new()
        .route("/info", get(info))
        .with_state(estado);

    // ...
}

async fn info(State(estado): State<Arc<AppState>>) -> String {
    format!("App: {}", estado.nome_app)
}

Middleware com Tower

A integração com Tower é uma das maiores vantagens do Axum. Você pode aplicar middleware por rota, por grupo de rotas ou globalmente. O ecossistema async do Rust em 2026 torna isso ainda mais poderoso.

use axum::middleware;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
use std::time::Duration;

let app = Router::new()
    .route("/api/usuarios", get(listar_usuarios))
    .route("/api/usuarios/:id", get(buscar_usuario))
    .layer(CorsLayer::permissive())
    .layer(TraceLayer::new_for_http())
    .layer(middleware::from_fn(autenticacao_middleware));

O tower-http fornece middlewares prontos para CORS, compressão, rate limiting, logging e muito mais — todos testados em produção por milhares de serviços.

Tratamento de Erros Robusto

O Axum permite criar tipos de erro personalizados que se convertem automaticamente em respostas HTTP:

use axum::{http::StatusCode, response::IntoResponse};

enum AppError {
    NaoEncontrado,
    ErroInterno(String),
    NaoAutorizado,
}

impl IntoResponse for AppError {
    fn into_response(self) -> axum::response::Response {
        let (status, mensagem) = match self {
            AppError::NaoEncontrado => (
                StatusCode::NOT_FOUND,
                "Recurso não encontrado".to_string(),
            ),
            AppError::ErroInterno(msg) => (
                StatusCode::INTERNAL_SERVER_ERROR,
                format!("Erro interno: {msg}"),
            ),
            AppError::NaoAutorizado => (
                StatusCode::UNAUTHORIZED,
                "Acesso não autorizado".to_string(),
            ),
        };

        (status, Json(serde_json::json!({ "erro": mensagem }))).into_response()
    }
}

Integração com Banco de Dados via SQLx

Para aplicações reais, a integração com banco de dados é essencial. O SQLx é a escolha natural — oferece queries verificadas em tempo de compilação:

use sqlx::PgPool;

struct AppState {
    pool: PgPool,
}

async fn listar_usuarios(
    State(state): State<Arc<AppState>>,
) -> Result<Json<Vec<Usuario>>, AppError> {
    let usuarios = sqlx::query_as!(
        Usuario,
        "SELECT id, nome, email FROM usuarios ORDER BY id"
    )
    .fetch_all(&state.pool)
    .await
    .map_err(|e| AppError::ErroInterno(e.to_string()))?;

    Ok(Json(usuarios))
}

Comparação: Axum vs Actix-Web vs Rocket vs Warp

CaracterísticaAxumActix-WebRocketWarp
RuntimeTokioTokio (via actix-rt)TokioTokio
MiddlewareTower (ecossistema)PróprioFairingsFilters
MacrosMínimasDecoradoresDecoradoresNenhuma
PerformanceExcelenteExcelenteBoaMuito boa
Curva de aprendizadoModeradaModeradaFácilÍngreme
EcossistemaTower + TokioPróprioPróprioPróprio
Popularidade 2026⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
ProduçãoAWS, DiscordMicrosoft, SamsungProjetos menoresNicho

O Axum lidera por combinar performance, ergonomia e o maior ecossistema de middleware disponível. Para um panorama completo do ecossistema, veja nosso artigo sobre o ecossistema Rust em 2026.

Padrões de Autenticação

Um padrão comum em APIs de produção é a autenticação via JWT:

use axum::{
    extract::FromRequestParts,
    http::request::Parts,
};

struct UsuarioAutenticado {
    id: u64,
    email: String,
}

#[axum::async_trait]
impl<S> FromRequestParts<S> for UsuarioAutenticado
where
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request_parts(
        parts: &mut Parts,
        _state: &S,
    ) -> Result<Self, Self::Rejection> {
        let header = parts
            .headers
            .get("Authorization")
            .ok_or(AppError::NaoAutorizado)?;

        // Validar JWT e extrair claims
        // ...
        Ok(UsuarioAutenticado { id: 1, email: "user@example.com".into() })
    }
}

// O handler recebe o usuário autenticado automaticamente
async fn perfil(usuario: UsuarioAutenticado) -> Json<serde_json::Value> {
    Json(serde_json::json!({
        "id": usuario.id,
        "email": usuario.email
    }))
}

Conclusão

O Axum representou uma mudança de paradigma no desenvolvimento web com Rust. Em vez de competir reinventando abstrações, ele abraçou o ecossistema existente do Tokio e Tower, criando uma base sólida que a comunidade rapidamente adotou. Se você está começando com Rust para web ou migrando de outro framework, o Axum em 2026 é a escolha mais segura e produtiva.

Para programadores vindo de Go, a transição é natural — veja nosso guia Rust para Programadores Go para comparações detalhadas. E para entender como o async funciona por baixo dos panos, não deixe de ler sobre o ecossistema async do Rust.

Veja Também