Documentation Index Fetch the complete documentation index at: https://mintlify.com/hypertekorg/hyperstack/llms.txt
Use this file to discover all available pages before exploring further.
The Rust SDK provides a high-performance client for connecting to Hyperstack servers. Built on Tokio and tokio-tungstenite, it offers async streaming with zero-cost abstractions and compile-time type safety.
Installation
Add to your Cargo.toml:
[ dependencies ]
hyperstack-sdk = "0.5"
TLS Options
By default, the SDK uses rustls for TLS. You can switch to native TLS:
[ dependencies ]
hyperstack-sdk = { version = "0.5" , default-features = false , features = [ "native-tls" ] }
Requirements
Rust 1.70+
Tokio runtime
A generated SDK crate from the Hyperstack CLI
Quick Start
Basic Connection
use hyperstack_sdk :: prelude ::* ;
use my_stack :: { PumpfunToken , PumpfunTokenEntity };
#[tokio :: main]
async fn main () -> anyhow :: Result <()> {
let hs = HyperStack :: connect ( "wss://mainnet.hyperstack.xyz" ) . await ? ;
// List all entities
let tokens = hs . list :: < PumpfunTokenEntity >() . await ;
println! ( "Found {} tokens" , tokens . len ());
Ok (())
}
Stream Updates
Streams are lazy - calling watch() returns immediately without subscribing. The subscription happens automatically on first poll:
use hyperstack_sdk :: prelude ::* ;
#[tokio :: main]
async fn main () -> anyhow :: Result <()> {
let hs = HyperStack :: connect ( "wss://mainnet.hyperstack.xyz" ) . await ? ;
let mut stream = hs . watch :: < PumpfunTokenEntity >();
while let Some ( update ) = stream . next () . await {
match update {
Update :: Upsert { key , data } => println! ( "Updated {}" , key ),
Update :: Patch { key , data } => println! ( "Patched {}" , key ),
Update :: Delete { key } => println! ( "Deleted {}" , key ),
}
}
Ok (())
}
The prelude module re-exports all commonly needed types including StreamExt, so you don’t need separate imports from futures_util.
API Reference
HyperStack Client
Simple Connection
let hs = HyperStack :: connect ( "wss://example.com" ) . await ? ;
Advanced Configuration
use std :: time :: Duration ;
let hs = HyperStack :: builder ()
. url ( "wss://example.com" )
. auto_reconnect ( true )
. max_reconnect_attempts ( 10 )
. ping_interval ( Duration :: from_secs ( 30 ))
. initial_data_timeout ( Duration :: from_secs ( 5 ))
. connect ()
. await ? ;
Core Methods
get
async fn get<E: Entity>(key: &str) -> Option<E::Data>
Get a single entity by key. let token = hs . get :: < PumpfunTokenEntity >( "token-123" ) . await ;
list
async fn list<E: Entity>() -> Vec<E::Data>
Get all entities of type E. let tokens = hs . list :: < PumpfunTokenEntity >() . await ;
println! ( "Found {} tokens" , tokens . len ());
watch
fn watch<E: Entity>() -> EntityStream<E::Data>
Stream all updates (lazy - no .await needed). let mut stream = hs . watch :: < PumpfunTokenEntity >();
while let Some ( update ) = stream . next () . await {
// Handle update
}
watch_key
fn watch_key<E: Entity>(key: &str) -> EntityStream<E::Data>
Stream updates for a specific key (lazy). let mut stream = hs . watch_key :: < PumpfunTokenEntity >( "token-123" );
while let Some ( update ) = stream . next () . await {
println! ( "Token 123 updated: {:?}" , update . data ());
}
watch_keys
fn watch_keys<E: Entity>(keys: &[&str]) -> EntityStream<E::Data>
Stream updates for multiple keys (lazy). let mut stream = hs . watch_keys :: < PumpfunTokenEntity >( & [ "token-1" , "token-2" ]);
watch_rich
fn watch_rich<E: Entity>() -> RichEntityStream<E::Data>
Stream with before/after values (lazy). let mut stream = hs . watch_rich :: < PumpfunTokenEntity >();
while let Some ( update ) = stream . next () . await {
match update {
RichUpdate :: Updated { before , after , .. } => {
println! ( "Changed from {:?} to {:?}" , before , after );
}
_ => {}
}
}
watch_key_rich
fn watch_key_rich<E: Entity>(key: &str) -> RichEntityStream<E::Data>
Rich stream for specific key (lazy). let mut stream = hs . watch_key_rich :: < PumpfunTokenEntity >( "token-123" );
connection_state
async fn connection_state() -> ConnectionState
Get current connection state. let state = hs . connection_state () . await ;
match state {
ConnectionState :: Connected => println! ( "Connected" ),
ConnectionState :: Connecting => println! ( "Connecting..." ),
ConnectionState :: Reconnecting { attempt } => println! ( "Reconnecting (attempt {})" , attempt ),
ConnectionState :: Disconnected => println! ( "Disconnected" ),
ConnectionState :: Error => println! ( "Error" ),
}
Update Types
Update type
Standard updates from watch():
pub enum Update < T > {
Upsert { key : String , data : T }, // Full entity update
Patch { key : String , data : T }, // Partial update (merged)
Delete { key : String }, // Entity removed
}
Helper methods:
key() - Get the entity key
data() - Get data reference (if not Delete)
is_delete() - Check if delete variant
has_data() - Check if has data (not Delete)
into_data() - Consume and get data
into_key() - Consume and get key
map(f) - Transform data
RichUpdate type
Rich updates from watch_rich() with before/after diffs:
pub enum RichUpdate < T > {
Created { key : String , data : T },
Updated { key : String , before : T , after : T , patch : Option < Value > },
Deleted { key : String , last_known : Option < T > },
}
The Updated variant includes patch - the raw JSON of changed fields:
if update . has_patch_field ( "trading" ) {
// The trading field was modified
}
Stream Operators
All streams support chainable operators:
use std :: collections :: HashSet ;
let watchlist : HashSet < String > = /* tokens to watch */ ;
let mut price_alerts = hs
. watch_rich :: < PumpfunTokenEntity >()
. filter ( move | u | watchlist . contains ( u . key ()))
. filter_map ( | update | match update {
RichUpdate :: Updated { before , after , .. } => {
let prev = before . trading . last_trade_price . flatten () . unwrap_or ( 0.0 );
let curr = after . trading . last_trade_price . flatten () . unwrap_or ( 0.0 );
if prev > 0.0 {
let pct = ( curr - prev ) / prev * 100.0 ;
if pct . abs () > 0.1 {
return Some (( after . info . name . clone (), pct ));
}
}
None
}
_ => None ,
});
while let Some (( name , pct )) = price_alerts . next () . await {
println! ( "[PRICE] {:?} changed by {:.2}%" , name , pct );
}
Available Operators
Operator Description .filter(predicate)Keep only updates matching the predicate .filter_map(f)Filter and transform in one step .map(f)Transform each update
All operators are chainable and return streams that support the same operators.
Views API
The Views API provides a unified interface for accessing state, list, and derived views:
use hyperstack_sdk :: prelude ::* ;
use my_stack :: { OreRound , OreRoundViews };
#[tokio :: main]
async fn main () -> anyhow :: Result <()> {
let hs = HyperStack :: connect ( "wss://mainnet.hyperstack.xyz" ) . await ? ;
// Get a views accessor
let views = hs . views :: < OreRoundViews >();
// Access derived view (e.g., "latest" round)
let latest = views . latest () . get () . await ;
println! ( "Latest round: {:?}" , latest );
// Access list view
let all_rounds = views . list () . get () . await ;
println! ( "Found {} rounds" , all_rounds . len ());
// Access state view by key
let specific = views . state () . get ( "round_key" ) . await ;
// Watch derived view for updates
let mut stream = views . latest () . watch ();
while let Some ( update ) = stream . next () . await {
println! ( "Latest round updated: {:?}" , update );
}
Ok (())
}
View Types
| View Type | Access Pattern | Returns |
|-----------|---------------|---------||
| State | views.state().get(key) | Option<T> |
| List | views.list().get() | Vec<T> |
| Derived Single | views.{name}().get() | Option<T> |
| Derived Collection | views.{name}().get() | Vec<T> |
All view types support .watch() for streaming updates.
Understanding Option<Option<T>> Fields
Generated entity types often have fields typed as Option<Option<T>>. This represents the patch semantics of Hyperstack updates:
| Value | Meaning |
|-------|---------||
| None | Field was not included in this update (no change) |
| Some(None) | Field was explicitly set to null |
| Some(Some(value)) | Field has a concrete value |
This distinction matters for partial updates (patches). When the server sends a patch, only changed fields are included.
Working with nested options
// Access a nested optional field
let price = token . trading . last_trade_price . flatten () . unwrap_or ( 0.0 );
// Check if field was explicitly set (vs absent from patch)
match & token . reserves . current_price_sol {
None => println! ( "Price not in this update" ),
Some ( None ) => println! ( "Price explicitly cleared" ),
Some ( Some ( price )) => println! ( "Price: {}" , price ),
}
// Compare values in before/after
if before . trading . last_trade_price != after . trading . last_trade_price {
println! ( "Price changed!" );
}
Generating a Rust SDK
Use the Hyperstack CLI to generate a typed Rust SDK from your spec:
# Generate SDK crate
hs sdk create rust settlement-game
# With custom output directory
hs sdk create rust settlement-game --output ./crates/game-sdk
# With custom crate name
hs sdk create rust settlement-game --crate-name game-sdk
This generates a crate with:
generated/settlement-game-stack/
├── Cargo.toml
└── src/
├── lib.rs # Re-exports
├── types.rs # Data structs
└── entity.rs # Entity trait implementations
Add the generated crate to your Cargo.toml:
[ dependencies ]
hyperstack-sdk = "0.5"
settlement-game-stack = { path = "./generated/settlement-game-stack" }
Connection Management
Auto-Reconnection
The SDK automatically reconnects on connection loss with configurable backoff:
use std :: time :: Duration ;
let hs = HyperStack :: builder ()
. url ( "wss://example.com" )
. auto_reconnect ( true )
. reconnect_intervals ( vec! [
Duration :: from_secs ( 1 ),
Duration :: from_secs ( 2 ),
Duration :: from_secs ( 5 ),
Duration :: from_secs ( 10 ),
])
. max_reconnect_attempts ( 20 )
. connect ()
. await ? ;
Monitoring Connection State
let state = hs . connection_state () . await ;
match state {
ConnectionState :: Connected => println! ( "Ready" ),
ConnectionState :: Connecting => println! ( "Establishing connection..." ),
ConnectionState :: Reconnecting { attempt } => {
println! ( "Reconnecting (attempt {})..." , attempt )
}
ConnectionState :: Disconnected => println! ( "Not connected" ),
ConnectionState :: Error => println! ( "Connection error" ),
}
Examples
Price Alert System
use hyperstack_sdk :: prelude ::* ;
#[tokio :: main]
async fn main () -> anyhow :: Result <()> {
let hs = HyperStack :: connect ( "wss://mainnet.hyperstack.xyz" ) . await ? ;
let mut stream = hs . watch_rich :: < TokenEntity >()
. filter_map ( | update | match update {
RichUpdate :: Updated { before , after , key , .. } => {
let prev = before . price . unwrap_or ( 0.0 );
let curr = after . price . unwrap_or ( 0.0 );
let change_pct = (( curr - prev ) / prev ) * 100.0 ;
if change_pct . abs () > 5.0 {
Some (( key , change_pct ))
} else {
None
}
}
_ => None ,
});
while let Some (( token , change )) = stream . next () . await {
println! ( "Alert: {} changed by {:.2}%" , token , change );
}
Ok (())
}
Game Leaderboard
#[tokio :: main]
async fn main () -> anyhow :: Result <()> {
let hs = HyperStack :: connect ( "wss://mainnet.hyperstack.xyz" ) . await ? ;
// Get initial leaderboard
let mut players = hs . list :: < PlayerEntity >() . await ;
players . sort_by ( | a , b | b . score . cmp ( & a . score));
println! ( "Top 10 Players:" );
for ( i , player ) in players . iter () . take ( 10 ) . enumerate () {
println! ( "{}. {} - {} points" , i + 1 , player . name, player . score);
}
// Watch for score changes
let mut stream = hs . watch_rich :: < PlayerEntity >();
while let Some ( update ) = stream . next () . await {
if let RichUpdate :: Updated { before , after , .. } = update {
let diff = after . score - before . score;
if diff > 0 {
println! ( "{} scored {} points!" , after . name, diff );
}
}
}
Ok (())
}
Next Steps
CLI Reference Generate Rust SDKs with the CLI
TypeScript SDK Explore the TypeScript SDK
Stack API Learn about Stack API concepts
GitHub View source and examples