Hey everyone,
I’m trying to build a stable subscriber for the Notification API in Node.js using the ws library. The connection works fine initially, but when I simulate a network drop or the server closes the socket, my reconnect logic gets stuck in a loop that exhausts resources pretty quickly.
I have a basic retry mechanism with exponential backoff, but I’m hitting an issue where the ping event isn’t firing consistently during the reconnection phase, so I can’t tell if the server is actually alive before I try to send the subscription request again. Here’s the relevant chunk of my code:
function connect() {
const ws = new WebSocket(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
ws.on('open', () => {
console.log('Connected');
sendSubscription(ws);
});
ws.on('close', (code, reason) => {
console.log('Closed with code', code);
// Simple exponential backoff
setTimeout(() => {
connect();
}, Math.min(backoff, 30000));
backoff *= 2;
});
ws.on('error', (err) => {
console.error('Socket error', err.message);
});
}
The problem is that code 1006 (abnormal closure) triggers the reconnect, but sometimes the token has expired by the time the retry happens, leading to a 401 on the handshake. Should I be refreshing the OAuth token inside the close event before calling connect() again, or is there a better pattern for handling these abrupt disconnects? I’ve seen some examples using ping/pong to keep the connection alive, but I’m not sure how to integrate that with the subscription lifecycle.
The issue is likely that you’re not handling the WebSocket close event properly before attempting to reconnect, causing multiple connections to stack up. You need to ensure the old socket is fully closed and cleaned up. Here’s a cleaner way to handle reconnection with proper cleanup and backoff:
const WebSocket = require('ws');
const { PlatformClient } = require('genesys-cloud-purecloud-platform-client');
const reconnect = async (wsUrl, maxRetries = 10) => {
let retries = 0;
const connect = () => {
const ws = new WebSocket(wsUrl);
ws.on('open', () => {
console.log('Connected to Genesys Cloud');
retries = 0; // Reset retries on successful connection
});
ws.on('message', (data) => {
// Handle incoming messages
console.log('Received:', data.toString());
});
ws.on('ping', () => {
console.log('Ping received');
});
ws.on('close', (code, reason) => {
console.log(`Connection closed: ${code} - ${reason}`);
if (retries < maxRetries) {
const backoffTime = Math.pow(2, retries) * 1000;
console.log(`Reconnecting in ${backoffTime}ms...`);
setTimeout(connect, backoffTime);
retries++;
} else {
console.log('Max retries reached');
}
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
};
connect();
};
Make sure to authenticate properly with OAuth before connecting. Use the PlatformClient to get the access token:
const platformClient = new PlatformClient();
const auth = platformClient.auth;
auth.loginOAuthClientCredentials({
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
grantType: 'client_credentials',
scopes: ['notification:subscription:read']
}).then((response) => {
const token = response.result.access_token;
const wsUrl = `wss://api.us.genesyscloud.com/v2/analytics/events/ws?access_token=${token}`;
reconnect(wsUrl);
});