subscriptions
Real-time subscription patterns
Coming soon: Need support for this? Email support@dwellir.com and we will enable it for you.
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