Your First Stack
In this tutorial, you’ll build an end-to-end streaming data pipeline in 15 minutes. We’ll create an “Ore” stack that tracks ORE mining rounds and displays live round data in a React app.
What You’ll Build
Section titled “What You’ll Build”A full-stack streaming application consisting of:
- A Rust Definition: Defines how to transform on-chain ORE mining data into a queryable “OreRound” entity.
- A Cloud Deployment: A managed Hyperstack instance that processes the stream.
- A React Frontend: A live dashboard showing the latest ORE mining rounds that updates instantly as on-chain events occur.
Part 1: Create the Stack (Rust)
Section titled “Part 1: Create the Stack (Rust)”The stack definition is the heart of your application. It tells Hyperstack which on-chain data to watch and how to project it into a useful state.
1. Create a new Rust project
Section titled “1. Create a new Rust project”cargo new ore-stack --libcd ore-stack2. Add dependencies
Section titled “2. Add dependencies”Add hyperstack to your Cargo.toml:
[dependencies]hyperstack = "0.5.3"serde = { version = "1.0", features = ["derive"] }3. Get the ORE IDL
Section titled “3. Get the ORE IDL”Download the ORE program IDL file and place it in an idl/ directory:
mkdir -p idlcurl -o idl/ore.json https://docs.usehyperstack.com/ore.json4. Write your stack definition
Section titled “4. Write your stack definition”Open src/lib.rs and define your projection. We’ll create a simplified version of the ORE mining round tracker:
use hyperstack::prelude::*;
#[hyperstack(idl = "idl/ore.json")]pub mod ore_stream { use hyperstack::macros::Stream; use serde::{Deserialize, Serialize};
// The main entity representing an ORE mining round #[entity(name = "OreRound")] #[view(name = "latest", sort_by = "id.round_id", order = "desc")] pub struct OreRound { pub id: RoundId, pub state: RoundState, pub metrics: RoundMetrics, }
// Round identification with primary key #[derive(Debug, Clone, Serialize, Deserialize, Stream)] pub struct RoundId { #[map(ore_sdk::accounts::Round::id, primary_key, strategy = SetOnce)] pub round_id: u64,
#[map(ore_sdk::accounts::Round::__account_address, lookup_index, strategy = SetOnce)] pub round_address: String, }
// Round state fields - updated on every change #[derive(Debug, Clone, Serialize, Deserialize, Stream)] pub struct RoundState { #[map(ore_sdk::accounts::Round::expires_at, strategy = LastWrite)] pub expires_at: Option<u64>,
#[map(ore_sdk::accounts::Round::total_deployed, strategy = LastWrite)] pub total_deployed: Option<u64>,
#[map(ore_sdk::accounts::Round::total_winnings, strategy = LastWrite)] pub total_winnings: Option<u64>, }
// Aggregated metrics computed from transactions #[derive(Debug, Clone, Serialize, Deserialize, Stream)] pub struct RoundMetrics { // Count of deploy instructions for this round #[aggregate(from = ore_sdk::instructions::Deploy, strategy = Count, lookup_by = accounts::round)] pub deploy_count: Option<u64>,
// Sum of all deployed SOL amounts #[aggregate(from = ore_sdk::instructions::Deploy, field = amount, strategy = Sum, lookup_by = accounts::round)] pub total_deployed_sol: Option<u64>, }}5. Build to generate the stack file
Section titled “5. Build to generate the stack file”When you compile your code, Hyperstack macros automatically generate a .hyperstack/OreStream.stack.json file containing all entities in your stack.
cargo buildPart 2: Deploy to Hyperstack Cloud
Section titled “Part 2: Deploy to Hyperstack Cloud”Now let’s push your stack to the cloud. Hyperstack manages the infrastructure, so you don’t need to worry about scaling or WebSocket servers.
1. Initialize your project
Section titled “1. Initialize your project”This creates a hyperstack.toml file that links your local stack to the cloud.
hs init2. Push and Deploy
Section titled “2. Push and Deploy”The up command is a shortcut that pushes your stack, builds the container, and deploys it to a global cluster.
hs upExpected Output:
✔ Stack pushed (v1)✔ Build created (id: bld_123...)✔ Build completed🚀 Deployed to: wss://<your-stack-name>.stack.usehyperstack.comPart 3: Generate SDK and Connect
Section titled “Part 3: Generate SDK and Connect”Finally, let’s build the frontend. First, generate a typed SDK from your deployed stack, then connect using React or TypeScript.
1. Generate the SDK
Section titled “1. Generate the SDK”From your stack project directory (where you ran hs up):
hs sdk create typescript ore-stack --output ./sdkThis creates a typed SDK in ./sdk/ with full TypeScript support.
2. Set up your React app
Section titled “2. Set up your React app”npx create-react-app my-ore-app --template typescriptcd my-ore-appnpm install hyperstack-react zustandCopy the generated SDK into your React app:
cp -r ../ore-stack/sdk ./src/ore-sdk3. Configure the Provider
Section titled “3. Configure the Provider”Wrap your app in HyperstackProvider using the URL from hs up.
import { HyperstackProvider } from "hyperstack-react";
const root = ReactDOM.createRoot(document.getElementById("root")!);root.render( <HyperstackProvider websocketUrl="wss://<your-stack-name>.stack.usehyperstack.com"> <App /> </HyperstackProvider>,);4. Use the Generated Stack
Section titled “4. Use the Generated Stack”import { useHyperstack } from "hyperstack-react";import { OreStack } from "./ore-sdk";
export default function App() { const stack = useHyperstack(OreStack); const { data: rounds, isLoading } = stack.views.OreRound.latest.use({ take: 5, });
if (isLoading) return <div>Connecting to stream...</div>;
return ( <div> <h1>Latest ORE Mining Rounds</h1> {rounds?.map((round) => ( <div key={round.id.round_id} style={{ padding: "1rem", border: "1px solid #ccc", margin: "0.5rem 0", }} > <h3>Round #{round.id.round_id}</h3> <p>Address: {round.id.round_address}</p> <p> Total Deployed: {round.state.total_deployed?.toLocaleString()}{" "} lamports </p> <p> Total Winnings: {round.state.total_winnings?.toLocaleString()}{" "} lamports </p> <p>Deploy Count: {round.metrics.deploy_count || 0}</p> <p> Total SOL Deployed:{" "} {round.metrics.total_deployed_sol?.toLocaleString()} lamports </p> </div> ))} </div> );}2. Set up your project
Section titled “2. Set up your project”mkdir my-ore-app && cd my-ore-appnpm init -ynpm install hyperstack-typescript typescriptCopy the generated SDK:
cp -r ../ore-stack/sdk ./src/ore-sdk3. Connect and stream data
Section titled “3. Connect and stream data”import { HyperStack } from "hyperstack-typescript";import { OreStack, type OreRound } from "./ore-sdk";
async function main() { // Connect to your deployed stack using the generated SDK const hs = await HyperStack.connect( "wss://<your-stack-name>.stack.usehyperstack.com", { stack: OreStack }, );
console.log("Connected! Streaming ORE rounds...\n");
// Stream updates with full type safety for await (const update of hs.views.OreRound.latest.watch()) { if (update.type === "upsert") { const round = update.data; console.log(`Round #${round.id.round_id}:`); console.log(` Address: ${round.id.round_address}`); console.log( ` Total Deployed: ${round.state.total_deployed?.toLocaleString()} lamports`, ); console.log( ` Total Winnings: ${round.state.total_winnings?.toLocaleString()} lamports`, ); console.log(` Deploy Count: ${round.metrics.deploy_count || 0}`); console.log(""); } }}
main().catch(console.error);4. Run your app
Section titled “4. Run your app”npx tsx src/index.tsTroubleshooting
Section titled “Troubleshooting””Stack file not found”
Section titled “”Stack file not found””Ensure you ran cargo build in your Rust project. The #[hyperstack] macro generates the .stack.json file during compilation.
WebSocket connection fails
Section titled “WebSocket connection fails”- Check that the URL matches the output of
hs up. - Ensure your stack is successfully deployed by running
hs stack list.
Data is not appearing
Section titled “Data is not appearing”- Verify that the ORE program is emitting events and updating the Round accounts.
- Check the browser console for any subscription errors.
- Ensure your view path matches the entity name exactly (e.g.,
OreRound/latest).
Next Steps
Section titled “Next Steps”- Learn more about Stack Macros to build complex pipelines.
- Explore Population Strategies for different data access patterns.
- Check the CLI Reference for advanced deployment options.