How to fetch multiple with futures

💡 full source code is on github

foo.json

{
  "id": "foo",
  "type": "Cat",
  "weight": 123.45,
  "createdAt": "2022-09-01"
}

bar.json

{
  "id": "bar",
  "type": "Duck",
  "weight": 42.2424,
  "createdAt": "2022-08-01"
}

Cargo.toml

[package]
name = "fetch-multiple-futures"
version = "0.1.0"
edition = "2021"

[dependencies]
# Use No-std support https://serde.rs/no-std.html
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = "1.0"

# Use foe fetch, feature `json` for load json, `rustls-tls` for load via `TLS`.
reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] }

# Use for handy return Result.
anyhow = "1.0.65"

# Use for async.
tokio = { version ="1.22", features = ["full"] }
futures = "0.3.25"

main.rs

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

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct AnimalData {
    id: String,
    weight: f32,
    created_at: String,
}

// Shared client for each call.
async fn fetch_multiple_with_one_client_join_all(urls: &[&str]) -> anyhow::Result<Vec<AnimalData>> {
    // New shared client once.
    let client = Client::new();

    // How to use join_all.
    let results = future::join_all(urls.iter().map(|&url| {
        // Use shared client.
        let client = &client;
        async move {
            let resp = client.get(url).send().await?;
            resp.json::<AnimalData>().await
        }
    }))
    .await;

    // Return flattened results, silent if error.
    Ok(results
        // We use into_iter so we get Vec<AnimalData> instead of Vec<&AnimalData>
        .into_iter()
        .flatten()
        .collect::<Vec<_>>())
}

// Each client for each call.
async fn fetch_multiple_with_each_client_join_all(
    urls: &[&str],
) -> anyhow::Result<Vec<AnimalData>> {
    // How to use join_all.
    let results = future::join_all(urls.iter().map(|&url| async move {
        // Fetch each url with new client.
        reqwest::get(url).await?.json::<AnimalData>().await
    }))
    .await;

    // Return flattened results, silent if error.
    Ok(results
        // We use into_iter so we get Vec<AnimalData> instead of Vec<&AnimalData>
        .into_iter()
        .flatten()
        .collect::<Vec<_>>())
}

#[tokio::main]
async fn main() {
    let urls = [
        "https://raw.githubusercontent.com/gist-rs/book/main/examples/r4/30-fetch-multiple-futures/src/foo.json",
    "https://raw.githubusercontent.com/gist-rs/book/main/examples/r4/30-fetch-multiple-futures/src/bar.json"
    ];

    let json = fetch_multiple_with_one_client_join_all(&urls).await;
    println!("{json:#?}");

    let json = fetch_multiple_with_each_client_join_all(&urls).await;
    println!("{json:#?}");
}

🤷 reqwest+TLS is not runnable via Rust Playground so output is shown below.

Run
Ok(
    [
        AnimalData {
            id: "foo",
            weight: 123.45,
            created_at: "2022-09-01",
        },
        AnimalData {
            id: "bar",
            weight: 42.2424,
            created_at: "2022-08-01",
        },
    ],
)
Ok(
    [
        AnimalData {
            id: "foo",
            weight: 123.45,
            created_at: "2022-09-01",
        },
        AnimalData {
            id: "bar",
            weight: 42.2424,
            created_at: "2022-08-01",
        },
    ],
)