---
title: "Criar Servidor HTTP em Rust"
url: "https://rustlang.com.br/receitas/criar-servidor-http/"
markdown_url: "https://rustlang.com.br/receitas/criar-servidor-http.MD"
description: "Aprenda como criar um servidor HTTP em Rust com axum. Rotas, handlers, JSON responses, middleware e estado compartilhado. Exemplos completos."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Criar Servidor HTTP em Rust

Aprenda como criar um servidor HTTP em Rust com axum. Rotas, handlers, JSON responses, middleware e estado compartilhado. Exemplos completos.


# Criar Servidor HTTP em Rust

O `axum` é um framework web moderno e ergonômico, construído sobre o ecossistema `tokio` e `tower`. Ele oferece rotas tipadas, extração automática de parâmetros e excelente performance.

## Dependências

**Cargo.toml:**
```toml
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.5", features = ["cors"] }
```

## Servidor básico com rotas

Um servidor mínimo com diferentes rotas:

```rust
use axum::{routing::get, Router};

async fn index() -> &'static str {
    "Bem-vindo à API Rust!"
}

async fn saude() -> &'static str {
    "OK"
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(index))
        .route("/saude", get(saude));

    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();
}
```

**Para testar (em outro terminal):**
```
$ curl http://localhost:3000
Bem-vindo à API Rust!

$ curl http://localhost:3000/saude
OK
```

## Respostas JSON

Retorne dados estruturados como JSON:

```rust
use axum::{routing::get, Json, Router};
use serde::Serialize;

#[derive(Serialize)]
struct Produto {
    id: u32,
    nome: String,
    preco: f64,
    disponivel: bool,
}

#[derive(Serialize)]
struct ListaProdutos {
    total: usize,
    produtos: Vec<Produto>,
}

async fn listar_produtos() -> Json<ListaProdutos> {
    let produtos = vec![
        Produto {
            id: 1,
            nome: "Notebook".to_string(),
            preco: 4599.90,
            disponivel: true,
        },
        Produto {
            id: 2,
            nome: "Mouse".to_string(),
            preco: 89.99,
            disponivel: true,
        },
        Produto {
            id: 3,
            nome: "Monitor Ultrawide".to_string(),
            preco: 3299.00,
            disponivel: false,
        },
    ];

    let total = produtos.len();
    Json(ListaProdutos { total, produtos })
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/api/produtos", get(listar_produtos));

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

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

**Saída (curl):**
```json
{
  "total": 3,
  "produtos": [
    {"id": 1, "nome": "Notebook", "preco": 4599.90, "disponivel": true},
    {"id": 2, "nome": "Mouse", "preco": 89.99, "disponivel": true},
    {"id": 3, "nome": "Monitor Ultrawide", "preco": 3299.0, "disponivel": false}
  ]
}
```

## Receber JSON no body (POST)

Aceite dados JSON nas requisições:

```rust
use axum::{http::StatusCode, routing::{get, post}, Json, Router};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct NovoProduto {
    nome: String,
    preco: f64,
}

#[derive(Serialize)]
struct ProdutoCriado {
    id: u32,
    nome: String,
    preco: f64,
    mensagem: String,
}

async fn criar_produto(
    Json(novo): Json<NovoProduto>,
) -> (StatusCode, Json<ProdutoCriado>) {
    // Em uma aplicação real, salvaria no banco de dados
    let criado = ProdutoCriado {
        id: 42, // ID gerado
        nome: novo.nome,
        preco: novo.preco,
        mensagem: "Produto criado com sucesso!".to_string(),
    };

    (StatusCode::CREATED, Json(criado))
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/api/produtos", post(criar_produto));

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

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

**Para testar:**
```bash
curl -X POST http://localhost:3000/api/produtos \
  -H "Content-Type: application/json" \
  -d '{"nome": "Teclado Mecânico", "preco": 349.90}'
```

**Saída:**
```json
{
  "id": 42,
  "nome": "Teclado Mecânico",
  "preco": 349.9,
  "mensagem": "Produto criado com sucesso!"
}
```

## Parâmetros de rota e query

Extraia parâmetros da URL e query string:

```rust
use axum::{
    extract::{Path, Query},
    routing::get,
    Json, Router,
};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct FiltroQuery {
    pagina: Option<u32>,
    limite: Option<u32>,
    busca: Option<String>,
}

#[derive(Serialize)]
struct Resposta {
    mensagem: String,
}

// Parâmetro na URL: /usuarios/42
async fn buscar_usuario(Path(id): Path<u32>) -> Json<Resposta> {
    Json(Resposta {
        mensagem: format!("Usuário #{} encontrado", id),
    })
}

// Query string: /buscar?pagina=1&limite=10&busca=rust
async fn buscar(Query(filtro): Query<FiltroQuery>) -> Json<serde_json::Value> {
    let pagina = filtro.pagina.unwrap_or(1);
    let limite = filtro.limite.unwrap_or(10);
    let busca = filtro.busca.unwrap_or_default();

    Json(serde_json::json!({
        "pagina": pagina,
        "limite": limite,
        "busca": busca,
        "mensagem": format!("Buscando '{}', página {}, {} por página", busca, pagina, limite)
    }))
}

// Múltiplos parâmetros: /api/v1/categorias/eletronicos/produtos/42
async fn produto_por_categoria(
    Path((categoria, produto_id)): Path<(String, u32)>,
) -> Json<Resposta> {
    Json(Resposta {
        mensagem: format!("Produto #{} na categoria '{}'", produto_id, categoria),
    })
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/usuarios/{id}", get(buscar_usuario))
        .route("/buscar", get(buscar))
        .route("/categorias/{categoria}/produtos/{id}", get(produto_por_categoria));

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

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

**Saída (exemplos):**
```
GET /usuarios/42
{"mensagem": "Usuário #42 encontrado"}

GET /buscar?pagina=2&limite=5&busca=notebook
{"pagina":2,"limite":5,"busca":"notebook","mensagem":"Buscando 'notebook', página 2, 5 por página"}
```

## Estado compartilhado

Compartilhe dados entre handlers usando `State`:

```rust
use axum::{extract::State, routing::get, Json, Router};
use std::sync::{Arc, Mutex};
use serde::Serialize;

#[derive(Serialize, Clone)]
struct Estatisticas {
    requisicoes: u64,
    ultima_visita: String,
}

type AppState = Arc<Mutex<Estatisticas>>;

async fn estatisticas(State(estado): State<AppState>) -> Json<Estatisticas> {
    let mut stats = estado.lock().unwrap();
    stats.requisicoes += 1;
    stats.ultima_visita = chrono::Utc::now().to_string();
    Json(stats.clone())
}

#[tokio::main]
async fn main() {
    let estado = Arc::new(Mutex::new(Estatisticas {
        requisicoes: 0,
        ultima_visita: String::new(),
    }));

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

    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();
}
```

## Veja também

- [Fazer Requisição HTTP](/receitas/fazer-requisicao-http/) — construa o lado cliente
- [Parse JSON em Rust](/receitas/parse-json/) — processe JSON recebido no servidor
- [Serializar Struct para JSON](/receitas/serializar-json/) — prepare respostas JSON
- [Async/Await Básico](/receitas/async-await-basico/) — entenda o modelo assíncrono usado pelo axum
- [Conectar ao PostgreSQL](/receitas/conectar-postgresql/) — integre banco de dados no seu servidor
- Documentação da crate: [axum](https://docs.rs/axum/)
