This is a tutorial that aims to show how to use the popular rust library: reqwest using simple step by step examples.

What is reqwest?

It is an easy and powerful Rust HTTP Client.

Here are it’s main features:

  • Plain bodies, JSON, urlencoded, multipart
  • Customizable redirect policy
  • HTTP Proxies
  • HTTPS via system-native TLS (or optionally, rustls)
  • Cookie Store
  • WASM

Step 1: Install it

Install it by declaring it alongside Tokio as dependencies:

[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }

Step 2: Write Code

Here is an example:

use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resp = reqwest::get("https://httpbin.org/ip")
        .await?
        .json::<HashMap<String, String>>()
        .await?;
    println!("{:#?}", resp);
    Ok(())
}

Let’s look at more examples.

Example 1: Simple Example

This is using the tokio runtime. You’ll need the following dependency:

 tokio = { version = "1", features = ["full"] }

simple.rs


#[cfg(not(target_arch = "wasm32"))]
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let res = reqwest::get("https://hyper.rs").await?;

    println!("Status: {}", res.status());

    let body = res.text().await?;

    println!("Body:\n\n{}", body);

    Ok(())
}

// The [cfg(not(target_arch = "wasm32"))] above prevent building the tokio::main function
// for wasm32 target, because tokio isn't compatible with wasm32.
// If you aren't building for wasm32, you don't need that line.
// The two lines below avoid the "'main' function not found" error when building for wasm32 target.
#[cfg(target_arch = "wasm32")]
fn main() {}

Example 2: Blocking Example

blocking.rs

fn main() -> Result<(), Box<dyn std::error::Error>> {
    env_logger::init();

    println!("GET https://www.rust-lang.org");

    let mut res = reqwest::blocking::get("https://www.rust-lang.org/")?;

    println!("Status: {}", res.status());
    println!("Headers:\n{:?}", res.headers());

    // copy the response body directly to stdout
    res.copy_to(&mut std::io::stdout())?;

    println!("\n\nDone.");
    Ok(())
}

Example 3: POST Request with Form Data

A Short example of a POST request with form data.

form.rs

// This is using the tokio runtime. You'll need the following dependency:
//
// tokio = { version = "1", features = ["full"] }
#[cfg(not(target_arch = "wasm32"))]
#[tokio::main]
async fn main() {
    let response = reqwest::Client::new()
        .post("http://www.baidu.com")
        .form(&[("one", "1")])
        .send()
        .await
        .expect("send");
    println!("Response status {}", response.status());
}

// The [cfg(not(target_arch = "wasm32"))] above prevent building the tokio::main function
// for wasm32 target, because tokio isn't compatible with wasm32.
// If you aren't building for wasm32, you don't need that line.
// The two lines below avoid the "'main' function not found" error when building for wasm32 target.
#[cfg(target_arch = "wasm32")]
fn main() {}

Example 4: Send and Receive Dynamic JSON

This example illustrates the way to send and receive arbitrary JSON. This is useful for some ad-hoc experiments and situations when you don’t really care about the structure of the JSON and just need to display it or process it at runtime.

json_dynamic.rs

// This is using the tokio runtime. You'll need the following dependency:
//
// tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let echo_json: serde_json::Value = reqwest::Client::new()
        .post("https://jsonplaceholder.typicode.com/posts")
        .json(&serde_json::json!({
            "title": "Reqwest.rs",
            "body": "https://docs.rs/reqwest",
            "userId": 1
        }))
        .send()
        .await?
        .json()
        .await?;

    println!("{:#?}", echo_json);
    // Object(
    //     {
    //         "body": String(
    //             "https://docs.rs/reqwest"
    //         ),
    //         "id": Number(
    //             101
    //         ),
    //         "title": String(
    //             "Reqwest.rs"
    //         ),
    //         "userId": Number(
    //             1
    //         )
    //     }
    // )
    Ok(())
}

Example 5: Send and Receive Static JSON

This example illustrates the way to send and receive statically typed JSON. In contrast to the arbitrary JSON example, this brings up the full power of Rust compile-time type system guaranties though it requires a little bit
more code.

json_typed.rs

// These require the serde dependency.
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Post {
    id: Option<i32>,
    title: String,
    body: String,
    #[serde(rename = "userId")]
    user_id: i32,
}

// This is using the tokio runtime. You'll need the following dependency:
//
// tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let new_post = Post {
        id: None,
        title: "Reqwest.rs".into(),
        body: "https://docs.rs/reqwest".into(),
        user_id: 1,
    };
    let new_post: Post = reqwest::Client::new()
        .post("https://jsonplaceholder.typicode.com/posts")
        .json(&new_post)
        .send()
        .await?
        .json()
        .await?;

    println!("{:#?}", new_post);
    // Post {
    //     id: Some(
    //         101
    //     ),
    //     title: "Reqwest.rs",
    //     body: "https://docs.rs/reqwest",
    //     user_id: 1
    // }
    Ok(())
}

Reference

Read more here.

Categorized in: