RPC & Subscriptions
Typed HTTP JSON-RPC and WebSocket subscriptions with @solana/kit 7.0.0.
Recipe
Quick-reference recipe card - copy-paste ready.
import { createSolanaRpc, createSolanaRpcSubscriptions, address } from "@solana/kit";
const rpc = createSolanaRpc(process.env.RPC_URL!);
const balance = await rpc.getBalance(address("PUBKEY")).send();
const ws = createSolanaRpcSubscriptions(process.env.RPC_WSS_URL!);When to reach for this:
- Any on-chain read from TypeScript (balances, accounts, blocks).
- Live UI updates via account or signature subscriptions.
- Server-side reads in Next.js route handlers.
- Replacing untyped fetch calls to JSON-RPC.
Working Example
import { createSolanaRpc, createSolanaRpcSubscriptions, address } from "@solana/kit";
const rpc = createSolanaRpc("https://api.devnet.solana.com");
const rpcSubscriptions = createSolanaRpcSubscriptions("wss://api.devnet.solana.com");
async function watchBalance(owner: string) {
const acct = address(owner);
const { value } = await rpc.getBalance(acct).send();
console.log("initial", value);
const abort = new AbortController();
const stream = await rpcSubscriptions
.accountNotifications(acct, { commitment: "confirmed" })
.subscribe({ abortSignal: abort.signal });
for await (const n of stream) {
console.log("update", n.value.lamports);
}
}What this demonstrates:
- HTTP RPC for point-in-time reads with
.send(). - Matching WSS URL for push notifications.
AbortControllerfor cleanup.
Deep Dive
How It Works
createSolanaRpcwraps JSON-RPC 2.0 with typed methods and responses.createSolanaRpcSubscriptionsopens a WebSocket and exposes async iterable notifications.- Commitment on calls controls staleness vs finality.
- Provider URLs often embed API keys as query params.
Common RPC Methods
| Method | Use |
|---|---|
getBalance | SOL balance |
getAccountInfo | Raw account data |
getLatestBlockhash | Transaction lifetime |
simulateTransaction | Preflight errors |
sendTransaction | Submit signed tx |
TypeScript Notes
// Pass commitment on reads that power treasury UI
await rpc.getBalance(owner, { commitment: "finalized" }).send();Gotchas
- HTTP URL on WebSocket client - connection fails silently or throws. Fix: use
wss://from provider docs. - Public RPC rate limits - 429s under load. Fix: dedicated devnet/mainnet endpoint.
- Subscription leaks - tabs left open exhaust provider caps. Fix: abort on unmount.
- Assuming DAS on every RPC -
getAssetneeds DAS-enabled hosts. Fix: capability matrix per vendor. - Skipping
.send()- RPC builders are lazy until sent. Fix: always chain.send().
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Yellowstone gRPC | Indexer-scale ingestion | Simple wallet UI |
| Helius webhooks | Server-side event push | Pure browser client |
| HTTP polling | Rare updates | Sub-second balance UI |
| web3.js Connection | Legacy maintenance | Greenfield apps |
FAQs
Does this work on devnet?
Yes - point RPC and wallet at the same cluster.
Can I mix kit and web3.js?
Avoid in one module - migrate wholesale per route.
What about wallet adapters?
Wallets provide signers; kit builds messages.
How do I pin @solana/kit?
Lock 7.0.0 in package.json and CI.
Where are priority fees?
Compute budget instructions before send.
How to test?
Surfpool 0.12.0 or local validator with devnet clones.
Lamports or SOL?
Always bigint lamports in kit code.
Which commitment?
confirmed for UI; finalized for treasury.
Next.js server components?
Reads on server; signing in client components.
Debug failed txs?
Simulate, read logs, verify account owners.
Related
- RPC Basics - commitment and clusters
- @solana/kit Basics - kit primer
- Signing & Sending - submit path
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, and LiteSVM 0.6.x.