Call an API

Query the GitHub API

reqwest serde cat-network-programming cat-encoding cat-web-programming cat-web-programming::http-client

Queries GitHub stargazers API v3⮳ with reqwest::get⮳ to get list of all users who have marked a GitHub project with a star. reqwest::Response⮳ is deserialized with reqwest::Response::json⮳ into User objects implementing serde::Deserialize⮳.

tokio::main is used to set up the async executor and the process waits for reqwest::get to complete before processing the response into User instances.

use reqwest::Error;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct User {
    login: String,
    id: u32,
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    let request_url = format!(
        "https://api.github.com/repos/{owner}/{repo}/stargazers",
        owner = "john-cd",
        repo = "rust_howto"
    );
    println!("{}", request_url);

    let client = reqwest::Client::builder().user_agent("Rust-test").build()?;

    let response = client.get(&request_url).send().await?;

    let users: Vec<User> = response.json().await?;
    println!("{:?}", users);
    Ok(())
}

Check if an API Resource Exists

reqwest cat-network-programming cat-web-programming cat-web-programming::http-client

Query the GitHub Users Endpoint using a HEAD request reqwest::Client::head⮳ and then inspect the response code to determine success. This is a quick way to query a rest resource without needing to receive a body. reqwest::Client⮳ configured with reqwest::ClientBuilder::timeout⮳ ensures a request will not last longer than a timeout.

Due to both reqwest::ClientBuilder::build⮳ and reqwest::RequestBuilder::send⮳ returning reqwest::Error⮳ types, the shortcut reqwest::Result⮳ is used for the main function return type.

use std::time::Duration;

use reqwest::ClientBuilder;

#[tokio::main]
async fn main() -> reqwest::Result<()> {
    let user = "ferris-the-crab";
    let request_url = format!("https://api.github.com/users/{}", user);
    println!("{}", request_url);

    let timeout = Duration::new(5, 0);
    let client = ClientBuilder::new().timeout(timeout).build()?;
    let response = client.head(&request_url).send().await?;

    if response.status().is_success() {
        println!("{} is a user!", user);
    } else {
        println!("{} is not a user!", user);
    }

    Ok(())
}

Create and Delete a Gist with the GitHub API

reqwest serde cat-network-programming cat-encoding cat-web-programming cat-web-programming::http-client

Creates a gist with POST request to GitHub gists API v3⮳ using reqwest::Client::post⮳ and removes it with DELETE request using reqwest::Client::post⮳.

The reqwest::Client⮳ is responsible for details of both requests including URL, body and authentication. The POST body from reqwest::Client⮳ macro provides arbitrary JSON body. Call to reqwest::Client⮳ sets the request body. reqwest::Client⮳ handles authentication. The call to reqwest::Client⮳ synchronously executes the requests.

use std::collections::HashMap;
use std::env;

use anyhow::Result;
use reqwest::Client;
use serde::Deserialize;
use serde::Serialize;

#[derive(Deserialize, Serialize, Debug)]
struct Post<'a> {
    description: &'a str,
    public: bool,
    files: HashMap<&'a str, Content<'a>>,
}

#[derive(Deserialize, Serialize, Debug)]
struct Content<'a> {
    content: &'a str,
}

#[derive(Deserialize, Debug)]
struct Gist {
    id: String,
    html_url: String,
}

#[tokio::main]
async fn main() -> Result<()> {
    let gh_user = env::var("GH_USER")?;
    let gh_pass = env::var("GH_PASS")?;

    // Example POST to the GitHub gists API
    let gist_body = Post {
        description: "the description for this gist",
        public: true,
        files: {
            let mut h = HashMap::new();
            h.insert(
                "main.rs",
                Content {
                    content: r#"
fn main() { println!("hello world!");}
"#,
                },
            );
            h
        },
    };

    let request_url = "https://api.github.com/gists";
    let response = Client::new()
        .post(request_url)
        .basic_auth(gh_user.clone(), Some(gh_pass.clone()))
        .json(&gist_body)
        .send()
        .await?;

    let gist: Gist = response.json().await?;
    println!("Created {:?}", gist);

    let request_url = format!("{}/{}", request_url, gist.id);
    let response = Client::new()
        .delete(&request_url)
        .basic_auth(gh_user, Some(gh_pass))
        .send()
        .await?;

    println!(
        "Gist {} deleted! Status code: {}",
        gist.id,
        response.status()
    );
    Ok(())
}

The example uses HTTP basic auth⮳ in order to authorize access to GitHub API⮳. Typical use case would employ one of the much more complex OAuth⮳ authorization flows.

Consume a Paginated RESTful API

reqwest serde cat-network-programming cat-encoding cat-web-programming cat-web-programming::http-client

Wraps a paginated web API in a convenient Rust iterator. The iterator lazily fetches the next page of results from the remote server as it arrives at the end of each page.

// // COMING SOON

Handle a Rate-limited API

reqwest hyper cat-network-programming cat-web-programming cat-web-programming::http-client

This example uses the GitHub API - rate limiting⮳, as an example of how to handle remote server errors. This example checks for reqwest::StatusCode::FORBIDDEN⮳ If the response exceeds the rate limit, the example waits and retries.

// // COMING SOON