@qubic.org/bob
Live Qubic node client with WebSocket subscriptions, automatic reconnection, and catch-up buffering.
Introduction
Why a persistent connection?
The Qubic network produces a new tick roughly every second. Polling getTickInfo over HTTP works, but a WebSocket subscription is more efficient — one round-trip to subscribe, then zero-overhead push for every tick. @qubic.org/bob manages that connection lifecycle for you.
Topics
Bob supports two subscription topics:
| Topic | What it delivers |
|---|---|
"tickInfo" | Every new tick — number, epoch, duration, and initial tick |
"eventLogs" | Account event logs for a specific identity — transfers, asset changes, contract messages |
Automatic reconnection and resubscription
Bob reconnects automatically after a connection drop. With autoResubscribe: true, all active subscriptions are re-registered on reconnect — you don't call subscribe again. Without it, listen to the "connect" event and re-subscribe manually.
Catch-up buffering
After a reconnect, the server may send buffered ticks that arrived during the gap. The meta.isCatchUp flag in the "tickInfo" callback identifies these so you can skip or process them separately.
Installation
bun add @qubic.org/bobnpm install @qubic.org/bobpnpm add @qubic.org/bobAPI reference
| Page | What's covered |
|---|---|
| Connection | createBobClient, connect, disconnect, autoResubscribe, events |
| Subscriptions | subscribe("tickInfo"), subscribe("eventLogs"), unsubscribeAll |
All exports
import {
createBobClient,
} from "@qubic.org/bob"
const bob = createBobClient({
autoResubscribe: true,
reconnectDelayMs: 1000,
})
await bob.connect()Examples
Subscribe to live tick updates
import { createBobClient } from "@qubic.org/bob"
const bob = createBobClient({ autoResubscribe: true })
await bob.connect()
bob.subscribe("tickInfo", (tick, meta) => {
if (meta.isCatchUp) return // skip buffered ticks from reconnect gap
console.log(`Tick ${tick.tick} (epoch ${tick.epoch}, ${tick.duration}ms)`)
})
process.on("SIGINT", async () => {
await bob.disconnect()
process.exit(0)
})Subscribe to account event logs
import { createBobClient } from "@qubic.org/bob"
import { toIdentity } from "@qubic.org/types"
const bob = createBobClient({ autoResubscribe: true })
await bob.connect()
const identity = toIdentity("CFBMEMZOIDEXQAUXYYSZIURADQLAPWPMNJXQSNVQZAHYVOPYUKKJBJUCTVJL")
bob.subscribe("eventLogs", identity, (events) => {
for (const event of events) {
console.log(`Event type ${event.eventType} at tick ${event.tick}`)
console.log(`TX: ${event.txId}`)
}
})React to lifecycle events
import { createBobClient } from "@qubic.org/bob"
const bob = createBobClient()
bob.on("connect", () => console.log("Connected"))
bob.on("disconnect", ({ code }) => console.log("Disconnected:", code))
bob.on("reconnect", ({ attempt }) => console.log("Reconnecting, attempt", attempt))
bob.on("error", (err) => console.error("Error:", err.message))
await bob.connect()Watch multiple identities
import { createBobClient } from "@qubic.org/bob"
import { toIdentity } from "@qubic.org/types"
const bob = createBobClient({ autoResubscribe: true })
await bob.connect()
const watchlist = [
toIdentity("CFBMEMZOIDEX..."),
toIdentity("ANOTHERIDENT..."),
]
for (const identity of watchlist) {
bob.subscribe("eventLogs", identity, (events) => {
handleEvents(identity, events)
})
}