Loading page…
Rust walkthroughs
Loading page…
Reqwest is an ergonomic HTTP client built on Hyper. It provides both blocking and async APIs, automatic request/response handling, JSON serialization via serde, cookies, redirects, and TLS support. Reqwest simplifies HTTP interactions while offering powerful configuration options for production use.
Key features:
Reqwest is the go-to HTTP client for most Rust applications.
# Cargo.toml
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }use reqwest::Error;
#[tokio::main]
async fn main() -> Result<(), Error> {
// Simple GET request
let body = reqwest::get("https://httpbin.org/get").await?.text().await?;
println!("Body: {}", body);
Ok(())
}use reqwest::{Client, Error};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Error> {
// Create a configured client
let client = Client::builder()
.timeout(Duration::from_secs(10))
.user_agent("my-app/1.0")
.build()?;
// GET request
let response = client
.get("https://httpbin.org/get")
.send()
.await?;
println!("Status: {}", response.status());
println!("Headers: {:?}", response.headers());
// Response body as text
let text = response.text().await?;
println!("Body: {}", text);
Ok(())
}use reqwest::{Client, Error};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct HttpBinResponse {
args: std::collections::HashMap<String, String>,
url: String,
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = Client::new();
// Query parameters
let response = client
.get("https://httpbin.org/get")
.query(&[
("key", "value"),
("foo", "bar"),
])
.send()
.await?;
let json: HttpBinResponse = response.json().await?;
println!("URL: {}", json.url);
println!("Args: {:?}", json.args);
// URL-encoded form data
let response = client
.post("https://httpbin.org/post")
.form(&[
("username", "alice"),
("password", "secret"),
])
.send()
.await?;
println!("Form POST status: {}", response.status());
Ok(())
}use reqwest::{Client, Error};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Debug, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
#[derive(Debug, Deserialize)]
struct UserResponse {
data: User,
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = Client::new();
// POST JSON
let new_user = CreateUser {
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
};
let response = client
.post("https://reqres.in/api/users")
.json(&new_user)
.send()
.await?;
println!("Created: {}", response.status());
let created: User = response.json().await?;
println!("User: {:?}", created);
// GET JSON
let response = client
.get("https://reqres.in/api/users/2")
.send()
.await?;
let user_response: UserResponse = response.json().await?;
println!("Fetched user: {:?}", user_response.data);
Ok(())
}use reqwest::{Client, Error, header};
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = Client::new();
// Custom headers
let response = client
.get("https://httpbin.org/headers")
.header("X-Custom-Header", "custom-value")
.header("Accept", "application/json")
.send()
.await?;
println!("Status: {}", response.status());
// Bearer token authentication
let token = "your-api-token";
let response = client
.get("https://httpbin.org/bearer")
.bearer_auth(token)
.send()
.await?;
println!("Bearer auth status: {}", response.status());
// Basic authentication
let response = client
.get("https://httpbin.org/basic-auth/user/pass")
.basic_auth("user", Some("pass"))
.send()
.await?;
println!("Basic auth status: {}", response.status());
// Default headers for all requests
let client_with_headers = Client::builder()
.default_headers({
let mut headers = header::HeaderMap::new();
headers.insert("X-Api-Key", "my-api-key".parse().unwrap());
headers.insert("Accept", "application/json".parse().unwrap());
headers
})
.build()?;
let response = client_with_headers
.get("https://httpbin.org/headers")
.send()
.await?;
println!("Default headers status: {}", response.status());
Ok(())
}use reqwest::{Client, Error, StatusCode};
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = Client::new();
// Check status code
let response = client
.get("https://httpbin.org/status/404")
.send()
.await?;
match response.status() {
StatusCode::OK => println!("Success!"),
StatusCode::NOT_FOUND => println!("Not found"),
StatusCode::INTERNAL_SERVER_ERROR => println!("Server error"),
status => println!("Other status: {}", status),
}
// Error on non-success status
let response = client
.get("https://httpbin.org/get")
.send()
.await?;
// .error_for_status() returns Err for 4xx/5xx
let response = response.error_for_status()?;
println!("Request succeeded: {}", response.status());
// Chain error handling
let result = client
.get("https://httpbin.org/status/500")
.send()
.await?
.error_for_status();
match result {
Ok(resp) => println!("Success: {}", resp.status()),
Err(e) => {
if e.is_status() {
println!("HTTP error: {}", e.status().unwrap());
} else if e.is_timeout() {
println!("Request timed out");
} else if e.is_connect() {
println!("Connection failed");
} else {
println!("Error: {}", e);
}
}
}
Ok(())
}use reqwest::{Client, Error};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = Client::builder()
.timeout(Duration::from_secs(5))
.connect_timeout(Duration::from_secs(2))
.pool_idle_timeout(Duration::from_secs(30))
.pool_max_idle_per_host(5)
.user_agent("my-app/1.0")
.build()?;
// Request-level timeout
let response = client
.get("https://httpbin.org/delay/1")
.timeout(Duration::from_secs(2))
.send()
.await;
match response {
Ok(resp) => println!("Got response: {}", resp.status()),
Err(e) if e.is_timeout() => println!("Request timed out!"),
Err(e) => println!("Error: {}", e),
}
Ok(())
}use reqwest::{Client, Error, multipart};
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = Client::new();
// Multipart form with text and file
let form = multipart::Form::new()
.text("field1", "value1")
.text("field2", "value2")
.part("file", multipart::Part::bytes(b"file content".to_vec())
.file_name("test.txt")
.mime_str("text/plain")?);
let response = client
.post("https://httpbin.org/post")
.multipart(form)
.send()
.await?;
println!("Multipart upload status: {}", response.status());
// Upload file from disk (requires "stream" feature)
// let file = tokio::fs::File::open("example.txt").await?;
// let part = multipart::Part::stream(reqwest::Body::from(file))
// .file_name("example.txt")
// .mime_str("text/plain")?;
// let form = multipart::Form::new().part("file", part);
Ok(())
}use reqwest::{Client, Error, redirect};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Error> {
// Control redirect behavior
let client = Client::builder()
.redirect(redirect::Policy::limited(5))
.build()?;
let response = client
.get("https://httpbin.org/redirect/2")
.send()
.await?;
println!("Final URL: {}", response.url());
println!("Status: {}", response.status());
// Disable redirects
let no_redirect_client = Client::builder()
.redirect(redirect::Policy::none())
.build()?;
let response = no_redirect_client
.get("https://httpbin.org/redirect/1")
.send()
.await?;
println!("No redirect status: {}", response.status());
// Cookie store (requires "cookies" feature)
// let client = Client::builder()
// .cookie_store(true)
// .build()?;
Ok(())
}# Cargo.toml
[dependencies]
reqwest = { version = "0.11", features = ["blocking", "json"] }
serde = { version = "1", features = ["derive"] }use reqwest::blocking::Client;
use reqwest::Error;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct IpInfo {
ip: String,
}
fn main() -> Result<(), Error> {
let client = Client::new();
// Simple blocking GET
let body = client
.get("https://httpbin.org/ip")
.send()?;
let ip_info: IpInfo = body.json()?;
println!("Your IP: {}", ip_info.ip);
// POST with JSON
let response = client
.post("https://httpbin.org/post")
.json(&serde_json::json!({ "key": "value" }))
.send()?;
println!("Status: {}", response.status());
Ok(())
}use reqwest::Client;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::builder()
.timeout(Duration::from_secs(10))
.build()?;
let urls = vec![
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
"https://httpbin.org/delay/1",
];
// Fetch all URLs concurrently
let handles: Vec<_> = urls
.into_iter()
.map(|url| {
let client = client.clone();
tokio::spawn(async move {
let resp = client.get(url).send().await?;
println!("Fetched {} -> {}", url, resp.status());
Ok::<_, reqwest::Error>(resp)
})
})
.collect();
// Wait for all requests
for handle in handles {
handle.await??;
}
// Using futures::join! (requires futures crate)
// let (r1, r2, r3) = tokio::join!(
// client.get("https://httpbin.org/get").send(),
// client.get("https://httpbin.org/ip").send(),
// client.get("https://httpbin.org/headers").send(),
// );
Ok(())
}reqwest::get(url) for simple one-off GET requestsClient for reusable connections and configuration.text(), .json(), or .bytes() on response to get body.query(&[...]) for URL query parameters.form(&[...]) for URL-encoded form data.json(&struct) for JSON body (requires serde feature).header("name", "value") or .default_headers() on client.bearer_auth(token) or .basic_auth(user, pass).status(); use .error_for_status() to error on non-2xx.timeout() on client or individual requests.multipart() for file uploads and multipart forms.redirect() policyblocking feature for synchronous reqwest::blocking::ClientClient for concurrent requests across threads.is_timeout(), .is_status(), .is_connect() methods