Noções Básicas de Backends Web
11 exemplos para você começar com backends web em Rust - 8 básicos e 3 intermediários.
Pré-requisitos
# Cargo.toml
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.6", features = ["trace"] }- Axum 0.8 no Tokio 1.x é a escolha padrão para novos serviços HTTP assíncronos.
- Execute com
cargo runapós adicionar um ponto de entrada#[tokio::main].
Exemplos Básicos
1. Servidor Hello World
App Axum mínimo escutando na porta 3000.
use axum::{routing::get, Router};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, Axum" }));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}Router::new()compõe rotas em um único serviço.axum::serveexecuta o app em um listener Tokio.- Handlers são funções assíncronas que retornam tipos que implementam
IntoResponse.
Relacionado: Rotas e Handlers do Axum - rotas e métodos
2. Resposta JSON
Retorna JSON tipado de um handler.
use axum::{routing::get, Json, Router};
use serde::Serialize;
#[derive(Serialize)]
struct Health { status: &'static str }
async fn health() -> Json<Health> {
Json(Health { status: "ok" })
}
let app = Router::new().route("/health", get(health));Json<T>defineContent-Type: application/json.- Derive
Serializenos tipos de resposta. - Axum serializa com serde automaticamente.
Relacionado: Extratores e Estado - extrator
Json
3. Parâmetros de Caminho
Captura valores tipados da URL.
use axum::extract::Path;
async fn get_user(Path(id): Path<u64>) -> String {
format!("user {id}")
}- O extrator
Pathanalisa e valida segmentos de rota. - Tipos inválidos retornam 400 automaticamente.
- Nomes de parâmetros devem corresponder ao template de rota
/:id.
4. Strings de Consulta
Filtros opcionais da string de consulta.
use axum::extract::Query;
use serde::Deserialize;
#[derive(Deserialize)]
struct ListParams { limit: Option<u32>, q: Option<String> }
async fn list(Query(params): Query<ListParams>) -> String {
format!("limit={:?} q={:?}", params.limit, params.q)
}Querydesserializa em qualquer tipoDeserialize.- Campos ausentes se tornam
NoneparaOption. - Use
serde(default)para padrões sensatos.
5. Corpo JSON POST
Aceita um corpo de requisição tipado.
use axum::{routing::post, Json};
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateItem { name: String }
async fn create(Json(body): Json<CreateItem>) -> String {
format!("created {}", body.name)
}- O extrator
Jsondesserializa o corpo da requisição. - JSON malformado retorna 422 com detalhes do erro serde.
- Mantenha tipos de entrada e saída separados nas fronteiras da API.
Relacionado: Validação de Requisição - restrições de campo
6. Estado Compartilhado da Aplicação
Injeta um pool de banco de dados ou configuração via State.
use axum::extract::State;
use std::sync::Arc;
#[derive(Clone)]
struct AppState { db_url: Arc<String> }
async fn info(State(state): State<AppState>) -> String {
state.db_url.clone()
}State<T>requerT: Clone- envolva dados pesados emArc.- Passe o estado com
.with_state(state)no router. - Um tipo de estado por aplicação é o típico.
Relacionado: Extratores e Estado - padrões de estado
7. Aninhamento de Roteadores
Agrupa rotas sob um prefixo.
use axum::{routing::get, Router};
let users = Router::new()
.route("/", get(|| async { "list users" }))
.route("/:id", get(|| async { "get user" }));
let app = Router::new().nest("/users", users);nestmonta um sub-roteador em um prefixo de caminho.- Mantém APIs grandes organizadas por domínio.
- Mescle roteadores com
Router::mergepara grupos irmãos.
8. Roteamento de Métodos
Handlers diferentes por método HTTP em um único caminho.
use axum::routing::{get, post};
let app = Router::new().route("/items", get(list_items).post(create_item));- Encadeie
.get(),.post(),.delete()em uma rota. - Métodos não suportados retornam 405.
- Siga as convenções REST: GET para ler, POST para criar.
Relacionado: Rotas e Handlers do Axum - roteamento de métodos
Exemplos Intermediários
9. Camada de Rastreamento Tower
Adiciona middleware de log de requisições.
use tower_http::trace::TraceLayer;
let app = Router::new()
.route("/health", get(|| async { "ok" }))
.layer(TraceLayer::new_for_http());- Camadas
tower-httpenvolvem o roteador inteiro. - A camada de rastreamento registra método, caminho, status e latência.
- A ordem das camadas importa - camadas externas são executadas primeiro nas requisições.
Relacionado: Middleware e Tower - composição de camadas
10. Respostas de Erro Tipadas
Retorna JSON de erro consistente.
use axum::{http::StatusCode, response::IntoResponse, Json};
use serde::Serialize;
#[derive(Serialize)]
struct ApiError { code: &'static str, message: String }
impl IntoResponse for ApiError {
fn into_response(self) -> axum::response::Response {
(StatusCode::BAD_REQUEST, Json(self)).into_response()
}
}- Tipos de erro personalizados implementam
IntoResponse. - Emparelhe códigos de status com corpos JSON.
- Centralize o mapeamento de erros em um único enum.
Relacionado: Tratamento de Erros em Handlers - enums de erro
11. Desligamento Gracioso
Para de aceitar novas conexões ao receber SIGTERM.
use tokio::signal;
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(async {
signal::ctrl_c().await.ok();
})
.await
.unwrap();with_graceful_shutdowndrena requisições em andamento.- Escute por SIGTERM em contêineres, Ctrl+C localmente.
- Emparelhe com verificações de saúde para implantações sem tempo de inatividade.
Relacionado: Testando Serviços Web - testes de integração
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+.