# Como integrar a API de CPF em uma aplicação Rust com Axum

> Aprenda a integrar a API de consulta de CPF em uma aplicação web Rust usando o framework Axum com extractors e estado compartilhado.

**Publicado:** 09/11/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-integrar-api-cpf-rust-axum

---


Para integrar a API de CPF da CPFHub.io em uma aplicação Rust com Axum, basta criar um `AppState` com um cliente `reqwest` configurado, injetar a API key via variável de ambiente e chamar `GET https://api.cpfhub.io/cpf/{CPF}` com o header `x-api-key`. O Axum recebe o resultado nos handlers usando o extractor `State`, o que mantém o código idiomático e seguro. A latência típica é de ~900ms e o plano gratuito oferece 50 consultas mensais sem cartão de crédito.

## Introdução

O Axum é um framework web moderno para Rust, desenvolvido pela equipe do Tokio. Ele se destaca pela integração nativa com o ecossistema Tokio, uso de extractors para parsing de requisições e compatibilidade com a tower middleware stack. A documentação oficial do Axum está disponível em [docs.rs/axum](https://docs.rs/axum).

---

## Configurando o projeto

Crie o projeto e adicione as dependências necessárias:

```rust
// Cargo.toml
[package]
name = "cpf-axum-api"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tower-http = { version = "0.5", features = ["cors", "trace"] }
tracing = "0.1"
tracing-subscriber = "0.3"
```

| Dependência | Papel na aplicação |
|---|---|
| `axum` | Framework web com roteamento e extractors |
| `tower-http` | Middlewares de CORS e tracing |
| `tracing` | Logging estruturado |
| `reqwest` | Cliente HTTP para a API do CPFHub |

---

## Definindo o estado da aplicação

No Axum, o estado compartilhado é passado para os handlers através de extractors tipados:

```rust
use std::sync::Arc;
use reqwest::header::{HeaderMap, HeaderValue};
use serde::{Deserialize, Serialize};

#[derive(Clone)]
pub struct AppState {
 pub cpf_client: Arc<CpfClient>,
}

pub struct CpfClient {
 client: reqwest::Client,
 api_key: String,
}

#[derive(Debug, Deserialize)]
pub struct CpfApiResponse {
 pub success: bool,
 pub data: CpfApiData,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CpfApiData {
 pub cpf: String,
 pub name: String,
 pub name_upper: String,
 pub gender: String,
 pub birth_date: String,
 pub day: String,
 pub month: String,
 pub year: String,
}

#[derive(Serialize)]
pub struct CpfResult {
 pub valido: bool,
 pub cpf: String,
 pub nome: Option<String>,
 pub data_nascimento: Option<String>,
 pub genero: Option<String>,
 pub mensagem: String,
}

impl CpfClient {
 pub fn new(api_key: String) -> Self {
 let client = reqwest::Client::builder()
 .timeout(std::time::Duration::from_secs(10))
 .build()
 .expect("Falha ao criar cliente HTTP");

 Self { client, api_key }
 }

 pub async fn consultar(&self, cpf: &str) -> Result<CpfApiResponse, String> {
 let mut headers = HeaderMap::new();
 headers.insert(
 "x-api-key",
 HeaderValue::from_str(&self.api_key).map_err(|e| e.to_string())?,
 );

 let url = format!("https://api.cpfhub.io/cpf/{}", cpf);

 let response = self.client
 .get(&url)
 .headers(headers)
 .send()
 .await
 .map_err(|e| format!("Erro na requisição: {}", e))?;

 if !response.status().is_success() {
 return Err(format!("Status: {}", response.status()));
 }

 response.json::<CpfApiResponse>()
 .await
 .map_err(|e| format!("Erro JSON: {}", e))
 }
}
```

---

## Criando os handlers

Os handlers no Axum utilizam extractors para receber parâmetros e estado:

```rust
use axum::{
 extract::{Path, State},
 http::StatusCode,
 response::IntoResponse,
 Json,
};

async fn consultar_cpf(
 State(state): State<AppState>,
 Path(cpf): Path<String>,
) -> impl IntoResponse {
 let cpf_limpo: String = cpf.chars().filter(|c| c.is_ascii_digit()).collect();

 if cpf_limpo.len() != 11 {
 return (
 StatusCode::BAD_REQUEST,
 Json(CpfResult {
 valido: false,
 cpf: cpf_limpo,
 nome: None,
 data_nascimento: None,
 genero: None,
 mensagem: "CPF deve conter 11 dígitos".to_string(),
 }),
 );
 }

 match state.cpf_client.consultar(&cpf_limpo).await {
 Ok(dados) if dados.success => (
 StatusCode::OK,
 Json(CpfResult {
 valido: true,
 cpf: dados.data.cpf,
 nome: Some(dados.data.name),
 data_nascimento: Some(dados.data.birth_date),
 genero: Some(dados.data.gender),
 mensagem: "CPF encontrado".to_string(),
 }),
 ),
 Ok(_) => (
 StatusCode::NOT_FOUND,
 Json(CpfResult {
 valido: false,
 cpf: cpf_limpo,
 nome: None,
 data_nascimento: None,
 genero: None,
 mensagem: "CPF não encontrado".to_string(),
 }),
 ),
 Err(e) => (
 StatusCode::BAD_GATEWAY,
 Json(CpfResult {
 valido: false,
 cpf: cpf_limpo,
 nome: None,
 data_nascimento: None,
 genero: None,
 mensagem: format!("Erro: {}", e),
 }),
 ),
 }
}

async fn health_check() -> impl IntoResponse {
 Json(serde_json::json!({ "status": "ok" }))
}
```

---

## Configurando rotas e middlewares

Monte a aplicação Axum com roteamento, CORS e logging:

```rust
use axum::Router;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;

#[tokio::main]
async fn main() {
 tracing_subscriber::init();

 let api_key = std::env::var("CPFHUB_API_KEY")
 .expect("CPFHUB_API_KEY deve estar definida");

 let state = AppState {
 cpf_client: Arc::new(CpfClient::new(api_key)),
 };

 let app = Router::new()
 .route("/api/cpf/:cpf", axum::routing::get(consultar_cpf))
 .route("/health", axum::routing::get(health_check))
 .layer(CorsLayer::permissive())
 .layer(TraceLayer::new_for_http())
 .with_state(state);

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

 tracing::info!("Servidor iniciado em http://localhost:3000");
 axum::serve(listener, app).await.unwrap();
}
```

---

## Perguntas frequentes

### Como o Axum lida com o estado compartilhado para chamadas à API de CPF?

O Axum injeta o estado via o extractor `State<AppState>` nos handlers. O `CpfClient` é encapsulado em um `Arc` para permitir clonagem sem custo entre threads. Essa abordagem garante que um único cliente `reqwest` — com pool de conexões — seja reutilizado em todas as requisições, o que melhora a performance e evita overhead de inicialização.

### A API da CPFHub.io retorna erro 429 quando o limite de consultas é atingido?

Não. A API da CPFHub.io nunca bloqueia nem retorna HTTP 429 ao atingir o limite do plano. Quando o limite mensal é ultrapassado, cada consulta adicional é cobrada a R$0,15 automaticamente. Isso garante que sua aplicação Rust continue funcionando sem interrupções, independentemente do volume de requisições.

### Como armazenar a API key com segurança em uma aplicação Rust com Axum?

A prática recomendada é injetar a chave via variável de ambiente (`std::env::var("CPFHUB_API_KEY")`), nunca hardcoded no código-fonte. Em produção, utilize um secret manager como AWS Secrets Manager ou HashiCorp Vault. O [OWASP Cheat Sheet sobre segurança de APIs](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html) documenta boas práticas para gestão de credenciais em serviços web.

### Como testar o handler de consulta de CPF com Axum sem chamar a API real?

Crie um trait `CpfClientTrait` e implemente um mock para testes. O Axum facilita isso pois os handlers aceitam qualquer tipo que implemente `Clone`. Use `tower::ServiceExt` e `axum::body::to_bytes` para simular requisições HTTP nos testes de integração sem subir um servidor real.

### Leia também

- [Como consumir a API de CPF em Rust usando reqwest](https://cpfhub.io/blog/como-consumir-api-cpf-rust-reqwest)
- [Como integrar a API de CPF em uma aplicação Rust com Actix Web](https://cpfhub.io/blog/como-integrar-api-cpf-rust-actix-web)
- [Como validar CPF em tempo real usando Rust e APIs REST](https://cpfhub.io/blog/como-validar-cpf-tempo-real-rust-apis-rest)
- [Como processar consultas de CPF de forma assíncrona em Rust com Tokio](https://cpfhub.io/blog/como-processar-consultas-cpf-assincrono-rust-tokio)

---

## Conclusão

O Axum oferece uma abordagem moderna e ergonômica para construir APIs em Rust. Seus extractors tipados, integração com Tokio e compatibilidade com o ecossistema tower tornam a integração com APIs externas como a CPFHub.io limpa e eficiente.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a validar CPFs na sua aplicação Rust com Axum em menos de 30 minutos.

