Skip to main content

quicknode_sdk/
lib.rs

1pub mod admin;
2pub mod config;
3pub mod errors;
4pub mod kvstore;
5pub mod streams;
6pub mod webhooks;
7
8pub use config::{
9    AdminConfig, HttpConfig, KvStoreConfig, SdkFullConfig, StreamsConfig, WebhooksConfig,
10};
11pub use kvstore::{
12    AddListItemParams, BulkSetsParams, CreateListParams, CreateSetParams, GetListData,
13    GetListParams, GetListResponse, GetListsData, GetListsParams, GetListsResponse, GetSetResponse,
14    GetSetsParams, GetSetsResponse, KvSetEntry, KvStoreApiClient, ListContainsItemResponse,
15    UpdateListParams,
16};
17
18use reqwest::header::{HeaderMap, HeaderValue};
19use reqwest::Client as ReqwestClient;
20use std::sync::Arc;
21
22use errors::SdkError;
23
24const DEFAULT_TIMEOUT_SECS: u64 = 30;
25
26// Using Arc for the inner config to keep as a cheap clone
27#[derive(Clone)]
28pub struct SdkConfig(Arc<SdkConfigInner>);
29
30impl std::fmt::Debug for SdkConfig {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.debug_struct("SdkConfig")
33            .field("api_key", &"[redacted]")
34            .field("admin_base_url", &self.0.admin.base_url)
35            .field("streams_base_url", &self.0.streams.base_url)
36            .field("webhooks_base_url", &self.0.webhooks.base_url)
37            .field("kvstore_base_url", &self.0.kvstore.base_url)
38            .finish()
39    }
40}
41
42struct SdkConfigInner {
43    http_client: ReqwestClient,
44    admin: admin::ResolvedAdminConfig,
45    streams: streams::ResolvedStreamsConfig,
46    webhooks: webhooks::ResolvedWebhooksConfig,
47    kvstore: kvstore::ResolvedKvStoreConfig,
48}
49
50impl SdkConfig {
51    pub fn new(config: &SdkFullConfig) -> Result<Self, SdkError> {
52        let mut builder = ReqwestClient::builder();
53
54        let timeout_secs = match &config.http {
55            Some(h) => match h.timeout_secs {
56                Some(secs) if secs < 0 => {
57                    return Err(SdkError::Config("timeout_secs must be non-negative".into()));
58                }
59                Some(secs) => secs as u64,
60                None => DEFAULT_TIMEOUT_SECS,
61            },
62            None => DEFAULT_TIMEOUT_SECS,
63        };
64        builder = builder.timeout(std::time::Duration::from_secs(timeout_secs));
65
66        if let Some(http) = &config.http {
67            if let Some(max_idle) = http.pool_max_idle_per_host {
68                builder = builder.pool_max_idle_per_host(max_idle as usize);
69            }
70        }
71
72        let mut default_headers = HeaderMap::new();
73        default_headers.insert(
74            reqwest::header::ACCEPT,
75            HeaderValue::from_static("application/json"),
76        );
77        default_headers.insert(
78            reqwest::header::CONTENT_TYPE,
79            HeaderValue::from_static("application/json"),
80        );
81        default_headers.insert(
82            "x-api-key",
83            HeaderValue::from_str(&config.api_key).map_err(|e| SdkError::Config(e.to_string()))?,
84        );
85        builder = builder.default_headers(default_headers);
86
87        let http_client = builder
88            .build()
89            .map_err(|e| SdkError::Config(e.to_string()))?;
90
91        Ok(Self(Arc::new(SdkConfigInner {
92            http_client,
93            admin: admin::ResolvedAdminConfig::from_config(config.admin.as_ref())?,
94            streams: streams::ResolvedStreamsConfig::from_config(config.streams.as_ref())?,
95            webhooks: webhooks::ResolvedWebhooksConfig::from_config(config.webhooks.as_ref())?,
96            kvstore: kvstore::ResolvedKvStoreConfig::from_config(config.kvstore.as_ref())?,
97        })))
98    }
99
100    pub(crate) fn http_client(&self) -> &ReqwestClient {
101        &self.0.http_client
102    }
103
104    pub(crate) fn admin(&self) -> &admin::ResolvedAdminConfig {
105        &self.0.admin
106    }
107
108    pub(crate) fn streams(&self) -> &streams::ResolvedStreamsConfig {
109        &self.0.streams
110    }
111
112    pub(crate) fn webhooks(&self) -> &webhooks::ResolvedWebhooksConfig {
113        &self.0.webhooks
114    }
115
116    pub(crate) fn kvstore(&self) -> &kvstore::ResolvedKvStoreConfig {
117        &self.0.kvstore
118    }
119}
120
121/// Top-level entry point for the QuickNode SDK. Holds sub-clients for each
122/// product area; all share a single HTTP client and API key.
123pub struct QuickNodeSdk {
124    /// Admin API client: manages endpoints, tags, teams, billing, usage,
125    /// metrics, security, and rate limits.
126    pub admin: admin::AdminApiClient,
127    /// Streams API client: creates and manages blockchain data streams.
128    pub streams: streams::StreamsApiClient,
129    /// Webhooks API client: creates and manages filter-template webhooks.
130    pub webhooks: webhooks::WebhooksApiClient,
131    /// Key-Value Store client: manages sets (single values) and lists
132    /// (ordered collections) under string keys.
133    pub kvstore: kvstore::KvStoreApiClient,
134}
135
136impl QuickNodeSdk {
137    /// Creates a new SDK instance from an explicit configuration.
138    pub fn new(config: &SdkFullConfig) -> Result<Self, SdkError> {
139        let sdk_config = SdkConfig::new(config)?;
140        Ok(Self {
141            admin: admin::AdminApiClient::new(sdk_config.clone()),
142            streams: streams::StreamsApiClient::new(sdk_config.clone()),
143            webhooks: webhooks::WebhooksApiClient::new(sdk_config.clone()),
144            kvstore: kvstore::KvStoreApiClient::new(sdk_config),
145        })
146    }
147
148    /// Creates a new SDK instance using configuration from environment variables.
149    pub fn from_env() -> Result<Self, SdkError> {
150        Self::new(&SdkFullConfig::from_env()?)
151    }
152}