WebSocket Subscriptions
Subscribe to real-time blockchain events using GraphQL subscriptions over WebSocket. Get instant notifications with minimal latency — perfect for live dashboards, block explorers, and trading applications.
Connection
Connect to the WebSocket endpoint:
wss://api.chainvue.io/graphqlWe use the graphql-ws protocol (not the older subscriptions-transport-ws). Include your API key as a connection parameter.
Available Subscriptions
newBlock
Subscribe to new blocks as they are confirmed:
subscription NewBlocks {
newBlock(chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq") {
height
hash
time
txCount
difficulty
size
miner
}
}newTransaction
Subscribe to new confirmed transactions:
subscription NewTransactions {
newTransaction(chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq") {
txid
blockHeight
blockHash
timestamp
valueIn
valueOut
fee
isCoinbase
}
}addressActivity
Watch specific addresses for incoming or outgoing transactions:
subscription WatchAddresses {
addressActivity(
chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
addresses: [
"RXL3YXG2ceaB6C5hfJcN4fvmLH2C34knhA",
"mike@"
]
) {
address
txid
type
amount
currencyId
currencyName
blockHeight
timestamp
}
}Tip: You can watch both regular addresses and VerusID names.
identityUpdates
Subscribe to VerusID changes:
subscription IdentityUpdates {
identityUpdates(
chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
names: ["mike", "alice"]
) {
name
fullyQualifiedName
identityAddress
operation
version
blockHeight
txid
primaryAddresses
}
}currencyPriceUpdates
Subscribe to price changes for basket currencies:
subscription PriceUpdates {
currencyPriceUpdates(
chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
currencyIds: ["iBridgeCurrencyId..."]
) {
currencyId
currencyName
price
priceChange24h
supply
blockHeight
timestamp
}
}largeTransactions
Get notified of whale movements:
subscription WhaleAlert {
largeTransactions(
chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
minValue: 10000
currencyId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq"
) {
txid
value
currencyName
fromAddresses
toAddresses
blockHeight
timestamp
}
}JavaScript Implementation
Using graphql-ws (Recommended)
import { createClient } from 'graphql-ws';
const client = createClient({
url: 'wss://api.chainvue.io/graphql',
connectionParams: {
authorization: 'Bearer YOUR_API_KEY',
},
// Automatic reconnection with exponential backoff
retryAttempts: Infinity,
shouldRetry: () => true,
// Keepalive ping every 30 seconds
keepAlive: 30000,
});
// Subscribe to new blocks
const unsubscribe = client.subscribe(
{
query: `
subscription {
newBlock(chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq") {
height
hash
txCount
time
}
}
`,
},
{
next: (data) => {
const block = data.data.newBlock;
console.log(`Block #${block.height}: ${block.txCount} transactions`);
},
error: (err) => {
console.error('Subscription error:', err);
},
complete: () => {
console.log('Subscription complete');
},
}
);
// Later: unsubscribe to clean up
// unsubscribe();React Hook Example
import { useEffect, useState, useCallback } from 'react';
import { createClient, Client } from 'graphql-ws';
// Create a singleton client
let client: Client | null = null;
function getClient() {
if (!client) {
client = createClient({
url: 'wss://api.chainvue.io/graphql',
connectionParams: {
authorization: `Bearer ${process.env.NEXT_PUBLIC_CHAINVUE_API_KEY}`,
},
});
}
return client;
}
// Custom hook for subscriptions
function useSubscription<T>(query: string, variables?: Record<string, any>) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const client = getClient();
const unsubscribe = client.subscribe(
{ query, variables },
{
next: (result) => setData(result.data as T),
error: (err) => setError(err as Error),
complete: () => console.log('Subscription complete'),
}
);
return () => unsubscribe();
}, [query, JSON.stringify(variables)]);
return { data, error };
}
// Usage in a component
function BlockMonitor() {
const { data, error } = useSubscription<{ newBlock: Block }>(`
subscription {
newBlock(chainId: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq") {
height
hash
txCount
}
}
`);
if (error) return <div>Error: {error.message}</div>;
if (!data) return <div>Waiting for blocks...</div>;
return (
<div>
<h2>Latest Block: #{data.newBlock.height}</h2>
<p>Transactions: {data.newBlock.txCount}</p>
</div>
);
}Node.js (Server-side)
import { createClient } from 'graphql-ws';
import WebSocket from 'ws';
const client = createClient({
url: 'wss://api.chainvue.io/graphql',
webSocketImpl: WebSocket,
connectionParams: {
authorization: 'Bearer YOUR_API_KEY',
},
});
// Multiple subscriptions on one connection
const subscriptions = [
// Watch blocks
client.subscribe(
{
query: `subscription { newBlock(chainId: "...") { height hash } }`,
},
{
next: (data) => processBlock(data.data.newBlock),
error: console.error,
}
),
// Watch addresses
client.subscribe(
{
query: `subscription {
addressActivity(chainId: "...", addresses: ["..."]) {
address txid amount
}
}`,
},
{
next: (data) => processActivity(data.data.addressActivity),
error: console.error,
}
),
];
// Cleanup on shutdown
process.on('SIGINT', () => {
subscriptions.forEach(unsub => unsub());
process.exit();
});Connection Management
Keepalive
WebSocket connections are kept alive with automatic ping/pong messages. Configure keepAlive in your client (default: 30 seconds).
Reconnection
The graphql-ws client automatically reconnects on disconnect. Configure retry behavior:
const client = createClient({
url: 'wss://api.chainvue.io/graphql',
connectionParams: { authorization: 'Bearer ...' },
// Retry configuration
retryAttempts: 10,
retryWait: async (retries) => {
// Exponential backoff: 1s, 2s, 4s, 8s, max 30s
const delay = Math.min(1000 * Math.pow(2, retries), 30000);
await new Promise(resolve => setTimeout(resolve, delay));
},
// Custom retry logic
shouldRetry: (errOrCloseEvent) => {
// Don't retry on auth errors
if (errOrCloseEvent?.code === 4401) return false;
return true;
},
// Called on successful connection
on: {
connected: () => console.log('Connected!'),
closed: () => console.log('Connection closed'),
error: (err) => console.error('Connection error:', err),
},
});Error Handling
Common error codes:
4400 — Bad request (invalid query)4401 — Unauthorized (invalid API key)4429 — Rate limited (too many subscriptions)4500 — Server errorConnection Limits
| Plan | Connections | Subscriptions per Connection |
|---|---|---|
| Free | 1 | 5 |
| Developer | 5 | 20 |
| Business | 25 | 50 |
| Enterprise | Unlimited | Unlimited |
Best Practices
- ✓Reuse connections — Create one client, use for multiple subscriptions
- ✓Handle reconnection — Your subscriptions auto-resume on reconnect
- ✓Clean up subscriptions — Always unsubscribe when components unmount
- ✓Request only needed fields — Reduces bandwidth and latency
- ✓Use variables — Makes queries reusable and type-safe