Coming soon: Need support for this? Email support@dwellir.com and we will enable it for you.
GraphQL Subscriptions
GraphQL subscriptions enable real-time data streaming from the Aptos blockchain, allowing applications to receive instant notifications when on-chain events occur. This feature is essential for responsive user interfaces, live dashboards, trading bots, and any application requiring immediate updates without polling.
Overview#
Unlike traditional queries that fetch data once, subscriptions establish persistent connections that stream updates as blockchain state changes. When a new block is added, transactions are processed, or specific events occur, subscribed clients receive notifications immediately, enabling truly reactive blockchain applications.
Core Subscription Patterns#
New Transactions#
subscription NewTransactions($address: String!) {
user_transactions(
where: {
_or: [
{ sender: { _eq: $address } },
{ receiver: { _eq: $address } }
]
},
order_by: { version: desc },
limit: 1
) {
hash
sender
version
success
gas_used
timestamp
}
}
Balance Changes#
subscription BalanceUpdates($owner: String!, $asset_type: String!) {
current_fungible_asset_balances(
where: {
owner_address: { _eq: $owner },
asset_type: { _eq: $asset_type }
}
) {
amount
last_transaction_version
last_transaction_timestamp
}
}
NFT Transfers#
subscription NftActivity($collection: String!) {
token_activities_v2(
where: {
token_data: {
collection_id: { _eq: $collection }
}
},
order_by: { transaction_version: desc },
limit: 1
) {
transaction_version
from_address
to_address
token_data_id
type
transaction_timestamp
}
}
New Blocks#
subscription NewBlocks {
ledger_infos(
order_by: { version: desc },
limit: 1
) {
chain_id
version
block_height
epoch
block_timestamp
}
}
Event Monitoring#
subscription EventStream($address: String!, $event_type: String!) {
events(
where: {
account_address: { _eq: $address },
type: { _eq: $event_type }
},
order_by: { transaction_version: desc },
limit: 1
) {
sequence_number
type
data
transaction_version
transaction_timestamp
}
}
Real-World Use Cases#
-
Live Wallets: Update balances, transaction histories, and token holdings in real-time as blockchain state changes without manual refreshing.
-
Trading Interfaces: Stream price updates, order fills, and liquidity changes instantly for responsive trading experiences on DEXes.
-
Notification Systems: Alert users immediately when they receive payments, NFT transfers, or other important on-chain events.
-
Live Dashboards: Display real-time protocol metrics, transaction volumes, active users, and other statistics with instant updates.
-
Gaming Applications: Stream game state changes, item transfers, and player actions in real-time for interactive blockchain games.
-
Monitoring Tools: Track smart contract interactions, detect anomalies, and monitor system health with instant event notifications.
Best Practices#
Handle Reconnections: Implement automatic reconnection logic with exponential backoff when subscription connections drop.
Manage Connection Limits: Be aware of concurrent subscription limits and reuse connections where possible.
Filter Aggressively: Use precise WHERE clauses to receive only relevant updates and reduce bandwidth usage.
Implement Debouncing: For rapid updates, debounce UI updates to prevent overwhelming the interface with changes.
Fallback to Polling: Have polling-based fallbacks for environments where WebSocket connections aren't available.
Validate Events: Always validate subscription payloads before processing to handle schema changes gracefully.
Monitor Performance: Track subscription latency and message rates to ensure responsive user experiences.
TypeScript Implementation#
import { ApolloClient, gql, InMemoryCache } from "@apollo/client";
import { WebSocketLink } from "@apollo/client/link/ws";
import { SubscriptionClient } from "subscriptions-transport-ws";
import ws from "ws";
// Create WebSocket client
const wsClient = new SubscriptionClient(
"wss://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql",
{
reconnect: true,
connectionParams: {
headers: {
"X-API-Key": "YOUR_API_KEY"
}
}
},
ws
);
const link = new WebSocketLink(wsClient);
const client = new ApolloClient({
link,
cache: new InMemoryCache()
});
// Subscribe to transactions
function subscribeToTransactions(address: string, callback: (tx: any) => void) {
const subscription = client.subscribe({
query: gql`
subscription NewTransactions($address: String!) {
user_transactions(
where: {
_or: [
{ sender: { _eq: $address } },
{ receiver: { _eq: $address } }
]
},
order_by: { version: desc },
limit: 1
) {
hash
sender
success
timestamp
}
}
`,
variables: { address }
});
return subscription.subscribe({
next: (result) => callback(result.data.user_transactions[0]),
error: (error) => console.error("Subscription error:", error)
});
}
// Usage
const unsubscribe = subscribeToTransactions("0x123...", (transaction) => {
console.log("New transaction:", transaction);
// Update UI with new transaction
});
// Clean up
// unsubscribe();
Advanced Patterns#
Combined Updates#
// Subscribe to multiple data streams
function subscribeToWallet(address: string) {
// Transactions
const txSub = client.subscribe({
query: TRANSACTION_SUBSCRIPTION,
variables: { address }
});
// Balance changes
const balanceSub = client.subscribe({
query: BALANCE_SUBSCRIPTION,
variables: { address }
});
// NFT transfers
const nftSub = client.subscribe({
query: NFT_SUBSCRIPTION,
variables: { address }
});
return {
unsubscribe: () => {
txSub.unsubscribe();
balanceSub.unsubscribe();
nftSub.unsubscribe();
}
};
}
Conditional Subscriptions#
// Only subscribe when needed
let subscription: any = null;
function startMonitoring(address: string) {
if (subscription) return;
subscription = subscribeToTransactions(address, (tx) => {
if (tx.success) {
notifyUser(`Transaction ${tx.hash} confirmed`);
}
});
}
function stopMonitoring() {
if (subscription) {
subscription.unsubscribe();
subscription = null;
}
}
Debounced Updates#
import { debounce } from "lodash";
const updateUI = debounce((data: any) => {
// Update UI with latest data
render(data);
}, 100);
subscribeToBalances(address, (balance) => {
updateUI(balance);
});
WebSocket Connection Management#
// Robust connection handling
class SubscriptionManager {
private client: SubscriptionClient;
private subscriptions: Map<string, any> = new Map();
constructor(url: string) {
this.client = new SubscriptionClient(url, {
reconnect: true,
reconnectionAttempts: 5,
connectionParams: {
headers: { "X-API-Key": process.env.API_KEY }
}
});
this.client.onReconnected(() => {
console.log("Reconnected - resubscribing...");
this.resubscribeAll();
});
}
subscribe(id: string, query: any, variables: any, callback: Function) {
const sub = this.client.request({ query, variables }).subscribe({
next: (data) => callback(data),
error: (error) => console.error(`Subscription ${id} error:`, error)
});
this.subscriptions.set(id, { sub, query, variables, callback });
return () => this.unsubscribe(id);
}
unsubscribe(id: string) {
const subscription = this.subscriptions.get(id);
if (subscription) {
subscription.sub.unsubscribe();
this.subscriptions.delete(id);
}
}
private resubscribeAll() {
this.subscriptions.forEach((sub, id) => {
this.unsubscribe(id);
this.subscribe(id, sub.query, sub.variables, sub.callback);
});
}
}
Related Concepts#
- GraphQL Overview - GraphQL API introduction
- Streaming API - Alternative real-time approach
- Aggregations - Statistical queries
- Token Activities - Transaction and transfer monitoring