Module 9 • Lesson 50

Building Async HTTP Clients

📚 10 min💻 Free🦀 nixus.pro

Building Async HTTP Clients

// Cargo.toml:
// reqwest = { version = "0.11", features = ["json"] }
// serde = { version = "1", features = ["derive"] }
// tokio = { version = "1", features = ["full"] }

use reqwest::Client;
use serde::{Deserialize, Serialize};

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

#[derive(Debug, Serialize)]
struct NewPost {
    title: String,
    body: String,
    user_id: u32,
}

async fn fetch_post(client: &Client, id: u32) -> Result {
    let url = format!("https://jsonplaceholder.typicode.com/posts/{}", id);
    client.get(&url).send().await?.json::().await
}

async fn create_post(client: &Client, post: &NewPost) -> Result {
    client
        .post("https://jsonplaceholder.typicode.com/posts")
        .json(post)
        .send()
        .await?
        .json::()
        .await
}

#[tokio::main]
async fn main() -> Result<(), Box> {
    let client = Client::new();

    // Fetch multiple posts concurrently
    let (p1, p2, p3) = tokio::join!(
        fetch_post(&client, 1),
        fetch_post(&client, 2),
        fetch_post(&client, 3),
    );

    for post in [p1?, p2?, p3?] {
        println!("Post {}: {}", post.id, post.title);
    }

    // Create a post
    let new_post = NewPost {
        title: "My Post".to_string(),
        body: "Written in Rust!".to_string(),
        user_id: 1,
    };
    let created = create_post(&client, &new_post).await?;
    println!("Created: {:?}", created);

    Ok(())
}

🎯 Practice

  1. Fetch all 100 posts from jsonplaceholder.typicode.com/posts concurrently using join_all
  2. Add error handling with retries: retry failed requests up to 3 times
  3. Add a rate limiter: max 10 concurrent requests at a time using tokio::sync::Semaphore

🎉 Key Takeaways