#HTTP Clients
The reqwest crate is the most popular HTTP client for Rust, supporting both blocking and async interfaces, TLS, cookies, and connection pooling.
#Simple GET Request
use reqwest;
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let body = reqwest::get("https://httpbin.org/get")
.await?
.text()
.await?;
println!("Response: {body}");
Ok(())
}
#JSON Requests and Responses
use reqwest;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Debug, Deserialize)]
struct UserResponse {
id: u64,
name: String,
email: String,
}
async fn create_user(user: &CreateUser) -> Result<UserResponse, reqwest::Error> {
let client = reqwest::Client::new();
let response = client
.post("https://api.example.com/users")
.json(user)
.send()
.await?
.json::<UserResponse>()
.await?;
Ok(response)
}
#Setting Headers and Authentication
use reqwest::header::{self, HeaderMap, HeaderValue};
fn build_client(api_key: &str) -> reqwest::Client {
let mut headers = HeaderMap::new();
headers.insert(
header::AUTHORIZATION,
HeaderValue::from_str(&format!("Bearer {api_key}")).unwrap(),
);
headers.insert(
header::ACCEPT,
HeaderValue::from_static("application/json"),
);
reqwest::Client::builder()
.default_headers(headers)
.timeout(std::time::Duration::from_secs(30))
.build()
.unwrap()
}
#Error Handling for HTTP
Check status codes and handle errors:
use reqwest::StatusCode;
async fn fetch_user(id: u64) -> Result<User, AppError> {
let response = reqwest::get(format!("https://api.example.com/users/{id}"))
.await
.map_err(AppError::Network)?;
match response.status() {
StatusCode::OK => {
let user = response.json().await.map_err(AppError::Parse)?;
Ok(user)
}
StatusCode::NOT_FOUND => Err(AppError::NotFound(id)),
status => Err(AppError::UnexpectedStatus(status.as_u16())),
}
}
#Downloading Files
Stream large files to disk without loading them entirely into memory:
use std::io::Write;
use reqwest;
use std::fs::File;
async fn download_file(url: &str, path: &str) -> Result<(), Box<dyn std::error::Error>> {
let response = reqwest::get(url).await?;
let bytes = response.bytes().await?;
let mut file = File::create(path)?;
file.write_all(&bytes)?;
Ok(())
}
#Retry Logic
Implement simple retry with exponential backoff:
use std::time::Duration;
use tokio::time::sleep;
async fn fetch_with_retry(url: &str, max_retries: u32) -> Result<String, reqwest::Error> {
let mut last_error = None;
for attempt in 0..max_retries {
match reqwest::get(url).await {
Ok(response) if response.status().is_success() => {
return response.text().await;
}
Ok(response) => {
eprintln!("Attempt {}: status {}", attempt + 1, response.status());
}
Err(error) => {
eprintln!("Attempt {}: {error}", attempt + 1);
last_error = Some(error);
}
}
let delay = Duration::from_millis(100 * 2u64.pow(attempt));
sleep(delay).await;
}
Err(last_error.unwrap())
}
#Summary
Use reqwest for HTTP clients in Rust. Create a Client instance and reuse it across requests for connection pooling. For production code, always handle status codes explicitly, set timeouts, and consider retry logic for transient failures.