If you're using TanStack Start or any React app with @tanstack/react-query,
you probably want Honeycluster responses to flow through the Query cache
alongside the rest of your data. This tutorial shows the shape of a reusable
useLedger hook — and how to keep the API key safely on your server side.
This tutorial shows both patterns:
https://honeycluster.io directly from the
browser. No proxy, no key, no server code./api/ledger/:index route instead, and let the proxy forward to the
private endpoint with the X-API-Key header attached. See the
Node.js proxy tutorial if you need to
build that layer.The hook structure is the same either way — only the URL differs.
TypeScript// lib/honeycluster.ts export interface LedgerSummary { ledger_index: number ledger_hash: string close_time_human: string transactions?: readonly unknown[] } // Public cluster — call directly from the browser. // For a private endpoint, swap this for `/api/ledger/${index}` and let // your proxy forward to the private hostname with the key attached. export async function fetchLedger(index: number): Promise<LedgerSummary> { const res = await fetch('https://honeycluster.io', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ method: 'ledger', params: [{ ledger_index: index, transactions: true, expand: true }], }), }) if (!res.ok) { const body = await res.json().catch(() => ({})) throw new Error(body?.message ?? `Ledger request failed: ${res.status}`) } const payload = await res.json() return payload.result.ledger as LedgerSummary }
Keep this in a plain module so it's testable without React.
TypeScript// hooks/useLedger.ts import { useQuery } from '@tanstack/react-query' import { fetchLedger, type LedgerSummary } from '~/lib/honeycluster' export function useLedger(index: number | null) { return useQuery<LedgerSummary, Error>({ queryKey: ['honeycluster', 'ledger', index], queryFn: () => fetchLedger(index!), enabled: index !== null, // Historical ledgers are immutable — cache effectively forever. staleTime: Infinity, gcTime: 1000 * 60 * 60, // 1 hour in memory }) }
Two important defaults:
staleTime: Infinity — historical ledger data never changes, so there's
no point refetching. New React Query consumers hit the cache instantly.enabled: index !== null — lets components call the hook
unconditionally and still defer the fetch until they have a valid index.Tsximport { useLedger } from '~/hooks/useLedger' export function LedgerCard({ index }: { index: number }) { const { data, isPending, error } = useLedger(index) if (isPending) return <p>Loading ledger {index}…</p> if (error) return <p className="text-danger9">{error.message}</p> if (!data) return null return ( <div> <h3>Ledger {data.ledger_index}</h3> <p>Closed at {data.close_time_human}</p> <p>{data.transactions?.length ?? 0} transactions</p> </div> ) }
For live streams — ledgerClosed, account subscriptions, etc. — React Query
isn't the right primitive. Those are push-based. Use a long-lived xrpl.js
client on your server and relay events to the browser via Server-Sent Events
or WebSockets. React Query can still hold the seeded "last known" value, and
your stream handler can call queryClient.setQueryData(['ledger', 'latest'], event)
to keep it fresh.
See Subscribe to Account Events for the server-side subscription pattern.