The Rust SDK, the primary and most complete Iggy client, and where its source lives.
The Rust SDK is the primary and most feature-complete client for Iggy. It is available on crates.io and the source code is part of the core repository.
Run from your application crate to add the published SDK:
cargo add iggyUse an SDK release compatible with your server. For unreleased changes, build both from the same checkout; the Getting Started guide shows the source dependency setup.
Prerequisites
The examples below log in with iggy/iggy. For a new local development instance, with no IGGY_ROOT_USERNAME or IGGY_ROOT_PASSWORD overrides, use:
cargo run --bin iggy-server -- --fresh --with-default-root-credentials--fresh wipes this replica's local data directory. Environment credentials take precedence over the flag, and bootstrap settings do not replace recovered credentials. In a cluster, a fresh replica can recover credentials from peers. Use disposable development data. A new standalone server without explicit credentials generates a root password; a new cluster requires explicit credentials.
High-level vs low-level API
The SDK provides two layers. The snippets below assume an async Tokio context with a Result return type.
High-level API (recommended) - the easiest way to get started. It handles connection management, auto-batching, consumer group lifecycle, offset commits, retry logic, and reconnection out of the box. Use IggyClient, IggyProducer, and IggyConsumer for the best developer experience.
use iggy::prelude::*;
// Connect with a connection string
let client = IggyClient::from_connection_string("iggy://iggy:iggy@127.0.0.1:8090")?;
client.connect().await?;
// Or use the builder
let client = IggyClientBuilder::new()
.with_tcp()
.with_server_address("127.0.0.1:8090".to_string())
.build()?;
client.connect().await?;
client.login_user("iggy", "iggy").await?;Low-level API - direct access to individual protocol operations. Useful when you need fine-grained control over every request, or when building custom abstractions on top.
The high-level API is built on top of the low-level one, so you can always drop down when needed.
Connection string
The connection string is the simplest way to configure the client. It supports all four transport protocols:
iggy://user:pass@host:port (TCP, default)
iggy+tcp://user:pass@host:port (TCP, explicit)
iggy+quic://user:pass@host:port (QUIC)
iggy+http://user:pass@host:port (HTTP)
iggy+ws://user:pass@host:port (WebSocket)TCP, QUIC, and WebSocket apply connection-string credentials on connect(). HTTP requires an explicit login and supports ordinary consumers only.
Options can be appended as query parameters:
iggy://iggy:iggy@127.0.0.1:8090?nodelay=true&heartbeat_interval=5s
iggy://iggy:iggy@127.0.0.1:8090?reconnection_retries=unlimited&heartbeat_interval=5sPersonal Access Tokens are also supported. Any credential without a colon is treated as a PAT. Paste the token exactly as the server returned it, with no prefix:
iggy://<personal-access-token>@127.0.0.1:8090See Connection Strings for the full per-transport option tables and defaults.
Quick example
use iggy::prelude::*;
use std::str::FromStr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = IggyClient::from_connection_string("iggy://iggy:iggy@127.0.0.1:8090")?;
client.connect().await?;
// Re-running this example is fine: an existing stream or topic is not an error.
match client.create_stream("my-stream").await {
Ok(_) | Err(IggyError::StreamNameAlreadyExists(_)) => {}
Err(e) => return Err(e.into()),
}
// Every topic knob rides `TopicCreateOptions`; fields left as `None`
// resolve against the server's defaults at admission.
match client
.create_topic(
&"my-stream".try_into()?,
"my-topic",
&TopicCreateOptions {
partitions_count: Some(1),
message_expiry: Some(IggyExpiry::NeverExpire),
..TopicCreateOptions::default()
},
)
.await
{
Ok(_) | Err(IggyError::TopicNameAlreadyExists(_, _)) => {}
Err(e) => return Err(e.into()),
}
let msg = IggyMessage::from_str("hello world")?;
client
.send_messages(
&"my-stream".try_into()?,
&"my-topic".try_into()?,
&Partitioning::partition_id(0),
&mut [msg],
)
.await?;
println!("Message sent");
let polled = client
.poll_messages(
&"my-stream".try_into()?,
&"my-topic".try_into()?,
Some(0),
&Consumer::default(),
&PollingStrategy::next(),
10,
true,
)
.await?;
for message in &polled.messages {
let payload = std::str::from_utf8(&message.payload)?;
println!("Offset: {}, Payload: {}", message.header.offset, payload);
}
Ok(())
}Partition IDs are 0-based: the first partition of a topic is partition 0. The consumer uses the default consumer identity and requests auto-commit; its offset can advance before application processing finishes. Use explicit offset storage after successful processing when that distinction matters.
For the full getting started tutorial, see Getting Started. For the high-level producer/consumer builders, see High-level SDK and Stream Builder.
