nym-http-api-client 1.20.4

Nym's HTTP API client, examples, and tests
Documentation

Nym HTTP API Client

Centralizes and implements the core API client functionality. This crate provides custom, configurable middleware for a re-usable HTTP client that takes advantage of connection pooling and other benefits provided by the [reqwest] Client.

Making GET requests

Create an HTTP Client and use it to make a GET request.

# use url::Url;
# use nym_http_api_client::{ApiClient, NO_PARAMS, HttpClientError};

# type Err = HttpClientError;
# async fn run() -> Result<(), Err> {
let url: Url = "https://nymvpn.com".parse()?;
let client = nym_http_api_client::Client::new(url, None);

// Send a get request to the `/v1/status` path with no query parameters.
let resp = client.send_get_request(&["v1", "status"], NO_PARAMS).await?;
let body = resp.text().await?;

println!("body = {body:?}");
# Ok(())
# }

JSON

There are also json helper methods that assist in executing requests that send or receive json. It can take any value that can be serialized into JSON.

# use std::collections::HashMap;
# use std::time::Duration;
use nym_http_api_client::{ApiClient, HttpClientError, NO_PARAMS};

# use serde::{Serialize, Deserialize};
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct ApiHealthResponse {
    pub status: ApiStatus,
    pub uptime: u64,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum ApiStatus {
    Up,
}

# type Err = HttpClientError;
# async fn run() -> Result<(), Err> {
// This will POST a body of `{"lang":"rust","body":"json"}`
let mut map = HashMap::new();
map.insert("lang", "rust");
map.insert("body", "json");

// Create a client using the ClientBuilder and set a custom timeout.
let client = nym_http_api_client::Client::builder("https://nymvpn.com")?
    .with_timeout(Duration::from_secs(10))
    .build()?;

// Send a POST request with our json `map` as the body and attempt to parse the body
// of the response as an ApiHealthResponse from json.
let res: ApiHealthResponse = client.post_json(&["v1", "status"], NO_PARAMS, &map)
    .await?;
# Ok(())
# }

Creating an ApiClient Wrapper

An example API implementation that relies on this crate for managing the HTTP client.

# use async_trait::async_trait;
use nym_http_api_client::{ApiClient, HttpClientError, NO_PARAMS};

mod routes {
    pub const API_VERSION: &str = "v1";
    pub const API_STATUS_ROUTES: &str = "api-status";
    pub const HEALTH: &str = "health";
}

mod responses {
    # use serde::{Serialize, Deserialize};
    #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
    pub struct ApiHealthResponse {
        pub status: ApiStatus,
        pub uptime: u64,
    }

    #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
    pub enum ApiStatus {
        Up,
    }
}

mod error {
    # use serde::{Serialize, Deserialize};
    # use core::fmt::{Display, Formatter, Result as FmtResult};
    #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
    pub struct RequestError {
        message: String,
    }

    impl Display for RequestError {
        fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
            Display::fmt(&self.message, f)
        }
    }
}

pub type SpecificAPIError = HttpClientError;

#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait SpecificApi: ApiClient {
    async fn health(&self) -> Result<responses::ApiHealthResponse, SpecificAPIError> {
        self.get_json(
            &[
                routes::API_VERSION,
                routes::API_STATUS_ROUTES,
                routes::HEALTH,
            ],
            NO_PARAMS,
        )
        .await
    }
}

impl<T: ApiClient> SpecificApi for T {}