Skip to content

Stack API Reference

The Stack API is the client-side interface to your Hyperstack spec. It provides type-safe access to views, transactions, and helpers.


import { defineStack, useHyperstack, createListView, createStateView } from 'hyperstack-react';
// 1. Define your stack
const MyStack = defineStack({
name: 'my-app',
views: { /* view definitions */ },
transactions: { /* optional */ },
helpers: { /* optional */ },
});
// 2. Use in components
function Component() {
const stack = useHyperstack(MyStack);
const { data } = stack.views.tokens.list.use();
}

Creates a typed stack definition.

function defineStack<TViews, TTxs, THelpers>(definition: {
name: string;
views: TViews;
transactions?: TTxs;
helpers?: THelpers;
}): StackDefinition
ParameterTypeRequiredDescription
namestringYesUnique identifier for the stack
viewsobjectYesView definitions grouped by entity
transactionsobjectNoTransaction builders with refresh logic
helpersobjectNoUtility functions
const TokenStack = defineStack({
name: 'token-tracker',
views: {
tokens: {
list: createListView<Token>('Token/list'),
state: createStateView<Token>('Token/state'),
},
trades: {
recent: createListView<Trade>('Trade/list', {
transform: (raw) => ({ ...raw, timestamp: new Date(raw.timestamp) })
}),
},
},
transactions: {
swap: {
build: (params: { mint: string; amount: number }) => ({
instruction: 'swap',
accounts: { mint: params.mint },
data: { amount: params.amount },
}),
refresh: [{ view: 'tokens/state', key: (p) => p.mint }],
},
},
helpers: {
formatPrice: (lamports: number) => (lamports / 1e9).toFixed(6) + ' SOL',
calculateMarketCap: (token: Token) => token.supply * token.price,
},
});

Main hook that provides access to a stack’s features.

function useHyperstack<TStack>(stack: TStack): StackClient<TStack>
interface StackClient<TStack> {
views: ViewInterface; // Real-time data subscriptions
tx: TransactionMethods; // Transaction submission
helpers: TStack['helpers']; // Utility functions
store: HyperstackStore; // Direct store access
runtime: HyperstackRuntime; // Runtime instance
}
function TokenDashboard() {
const stack = useHyperstack(TokenStack);
// Access views
const { data: tokens } = stack.views.tokens.list.use();
// Access helpers
const formatted = stack.helpers.formatPrice(1000000000);
// Access transactions
const { submit } = stack.tx.useMutation();
return <div>{/* ... */}</div>;
}

Creates a view that returns entities as an array.

function createListView<T>(
viewPath: string,
options?: ViewOptions<T>
): ListViewDefinition<T>

Usage:

// Basic
const tokenList = createListView<Token>('Token/list');
// With transform
const tokenList = createListView<Token>('Token/list', {
transform: (raw) => ({
...raw,
priceFormatted: formatPrice(raw.price),
})
});

In component:

const { data, isLoading, error, refresh } = stack.views.tokens.list.use({
limit: 50,
where: { volume: { gte: 1000 } },
});
// data: Token[] | undefined

Creates a view that returns a single entity by key.

function createStateView<T>(
viewPath: string,
options?: ViewOptions<T>
): StateViewDefinition<T>

Usage:

const tokenState = createStateView<Token>('Token/state');

In component:

const { data, isLoading, error } = stack.views.tokens.state.use({
key: mintAddress
});
// data: Token | undefined

interface ViewOptions<T> {
transform?: (data: any) => T;
}
OptionDescription
transformTransform raw data before returning
// Convert bigint strings to BigInt
createListView<Token>('Token/list', {
transform: (raw) => ({
...raw,
supply: BigInt(raw.supply),
})
});
// Flatten nested data
createStateView<Token>('Token/state', {
transform: (raw) => ({
mint: raw.id.mint,
name: raw.info.name,
volume: raw.trading.total_volume,
})
});

interface ListViewHookResult<T> {
data: T[] | undefined; // Array of entities
isLoading: boolean; // True while loading
error?: Error; // Error if failed
refresh: () => void; // Force refresh
}
interface StateViewHookResult<T> {
data: T | undefined; // Single entity
isLoading: boolean;
error?: Error;
refresh: () => void;
}

interface ListParams {
key?: string; // Filter by partition key
where?: WhereClause; // Field filters
limit?: number; // Max results
filters?: Record<string, string>; // Additional filters
}
interface WhereClause {
[field: string]: {
eq?: any; // Equals
gt?: any; // Greater than
gte?: any; // Greater than or equal
lt?: any; // Less than
lte?: any; // Less than or equal
};
}
// Limit results
stack.views.tokens.list.use({ limit: 10 })
// Filter by field
stack.views.tokens.list.use({
where: { volume: { gte: 10000 } }
})
// Multiple conditions
stack.views.tokens.list.use({
where: {
volume: { gte: 10000 },
created_at: { gt: Date.now() - 86400000 }
},
limit: 20
})

View paths follow the pattern: {EntityName}/{mode}

ModeReturnsExample
listT[]Token/list
stateTToken/state
kvRecord<string, T>Token/kv

The entity name must match your spec’s #[entity(name = "EntityName")].


interface TransactionDefinition<TParams> {
build: (params: TParams) => TransactionInstruction;
refresh?: RefreshSpec[];
}
interface RefreshSpec {
view: string;
key?: string | ((params: any) => string);
}
const MyStack = defineStack({
name: 'my-app',
views: { /* ... */ },
transactions: {
createToken: {
build: (params: { name: string; symbol: string }) => ({
instruction: 'createToken',
data: { name: params.name, symbol: params.symbol },
}),
// Refresh token list after transaction
refresh: [{ view: 'tokens/list' }],
},
updateToken: {
build: (params: { mint: string; newName: string }) => ({
instruction: 'updateToken',
accounts: { mint: params.mint },
data: { name: params.newName },
}),
// Refresh specific token state
refresh: [{ view: 'tokens/state', key: (p) => p.mint }],
},
},
});

Submit transactions with status tracking.

interface UseMutationReturn {
submit: (instruction: any) => Promise<string>;
status: 'idle' | 'pending' | 'success' | 'error';
error?: string;
signature?: string;
reset: () => void;
}
function CreateTokenButton() {
const stack = useHyperstack(MyStack);
const { submit, status, error, signature } = stack.tx.useMutation();
const handleCreate = async () => {
const instruction = stack.transactions.createToken.build({
name: 'My Token',
symbol: 'MTK',
});
try {
const sig = await submit(instruction);
console.log('Transaction:', sig);
} catch (e) {
console.error('Failed:', e);
}
};
return (
<div>
<button onClick={handleCreate} disabled={status === 'pending'}>
{status === 'pending' ? 'Creating...' : 'Create Token'}
</button>
{status === 'error' && <p>Error: {error}</p>}
{status === 'success' && <p>Success: {signature}</p>}
</div>
);
}

Helpers are utility functions bundled with your stack.

const TokenStack = defineStack({
name: 'tokens',
views: { /* ... */ },
helpers: {
formatPrice: (lamports: number) => `${(lamports / 1e9).toFixed(6)} SOL`,
formatSupply: (supply: bigint) => supply.toLocaleString(),
calculatePrice: (token: Token) =>
Number(token.sol_reserves) / Number(token.token_reserves),
},
});

Usage:

function TokenCard({ token }: { token: Token }) {
const stack = useHyperstack(TokenStack);
return (
<div>
<p>Price: {stack.helpers.formatPrice(token.price)}</p>
<p>Supply: {stack.helpers.formatSupply(token.supply)}</p>
</div>
);
}

Monitor WebSocket connection status.

import { useConnectionState } from 'hyperstack-react';
function ConnectionIndicator() {
const state = useConnectionState();
// 'disconnected' | 'connecting' | 'connected' | 'error' | 'reconnecting'
const colors = {
disconnected: 'gray',
connecting: 'yellow',
connected: 'green',
error: 'red',
reconnecting: 'orange',
};
return <span style={{ color: colors[state] }}>{state}</span>;
}

The Stack API is fully typed. TypeScript infers types from your stack definition:

// Types are inferred from createListView<Token>
const { data } = stack.views.tokens.list.use();
// data: Token[] | undefined
// Helper types are preserved
const formatted = stack.helpers.formatPrice(1000);
// formatted: string (inferred from helper return type)
// Transaction params are typed
stack.transactions.createToken.build({
name: 'Test', // TypeScript knows this is required
// symbol: missing - TypeScript error!
});

function TokenDetail({ mint }: { mint: string | null }) {
const stack = useHyperstack(TokenStack);
// Only subscribe when mint is available
const { data } = stack.views.tokens.state.use(
mint ? { key: mint } : undefined
);
if (!mint) return <p>Select a token</p>;
if (!data) return <p>Loading...</p>;
return <p>{data.name}</p>;
}
function Dashboard() {
const stack = useHyperstack(MyStack);
const { data: tokens } = stack.views.tokens.list.use();
const { data: stats } = stack.views.stats.state.use({ key: 'global' });
const isLoading = !tokens || !stats;
if (isLoading) return <Loading />;
return <DashboardView tokens={tokens} stats={stats} />;
}
function RefreshableList() {
const stack = useHyperstack(TokenStack);
const { data, refresh, isLoading } = stack.views.tokens.list.use();
return (
<div>
<button onClick={refresh} disabled={isLoading}>
{isLoading ? 'Refreshing...' : 'Refresh'}
</button>
{data?.map(token => <TokenRow key={token.mint} token={token} />)}
</div>
);
}