Tokio. Zero-cost.
No-std optional.
Async/await on Tokio, reqwest for HTTP, serde for serialization, thiserror for every failure mode. Feature-gated for minimal binary size.
cargo add.
Feature-gated.
Pull in only what you need. The streaming feature adds tokio-tungstenite. The cli feature adds clap. Default is just the REST client.
[dependencies]
relays-sdk = { version = "0.1", features = ["stream"] }
# Available features:
# default - REST client (reqwest + serde)
# stream - WebSocket event stream (tokio-tungstenite)
# cli - CLI helpers (clap)
# rustls - Use rustls instead of native-tls
# no-std - Core types only, no HTTP client
# Or add from the command line:
# cargo add relays-sdk --features streamBuilder pattern.
Sensible defaults.
Construct with Config or from environment. Connection pooling, automatic retries, and timeout configuration out of the box.
Strongly typed.
Exhaustive enums.
RecordType is an enum. The compiler enforces you handle every variant. No stringly-typed foot-guns.
thiserror.
Match arms.
Every API failure is a variant of RelaysError. Pattern-match on validation errors, rate limits, not-found, or network failures. No unwrap needed.
use relays_sdk::error::RelaysError;
match client.records("example.com")
.create(record).await
{
Ok(rec) => println!("Created {}", rec.id),
Err(RelaysError::Validation { field, reason }) =>
eprintln!("{field}: {reason}"),
Err(RelaysError::RateLimit { retry_after }) =>
tokio::time::sleep(retry_after).await,
Err(RelaysError::NotFound { resource, id }) =>
eprintln!("{resource} {id} not found"),
Err(RelaysError::Network(e)) =>
eprintln!("network: {e}"),
Err(e) => eprintln!("unexpected: {e}"),
}broadcast channel.
Fan out.
The stream feature connects a WebSocket and exposes events via a tokio broadcast channel. Multiple tasks can subscribe to the same stream. Automatic reconnection with resume tokens.
use relays_sdk::stream::{EventStream, StreamFilter};
use relays_sdk::events::Event;
let mut stream = client.events().stream(
StreamFilter::builder()
.zones(["example.com"])
.types(["dns.*", "monitor.*"])
.build(),
).await?;
while let Some(event) = stream.recv().await {
match event {
Event::DnsRecordCreated(data) => {
println!("New record: {}", data.record.name);
}
Event::MonitorDown(data) => {
alert(&data.monitor_id).await;
}
_ => {}
}
}
// Auto-reconnects with resume token.