Skip to content

Instantly share code, notes, and snippets.

@sesgoe
Last active May 30, 2024 09:26
Show Gist options
  • Save sesgoe/81aad6dd4b587d6bcc4d8937407ef601 to your computer and use it in GitHub Desktop.
Save sesgoe/81aad6dd4b587d6bcc4d8937407ef601 to your computer and use it in GitHub Desktop.
Retool Realtime WebSocket
const webSocket = new WebSocket('wss://echo.websocket.org');
//Event listeners are the pillars upon which WebSockets are built. This event fires when the WebSocket is considered 'OPEN',
//which means that it has connected successfully.
webSocket.addEventListener('open', function(event) {
console.log('websocket connected successfully') //log this into the browser console so we can check if the websocket connected
});
//This is a global reference to the websocket that we created. We need this because otherwise multiple JS
//queries can't access the same WebSocket object. If each query had its own `new WebSocket()`, the interactivity of this
//example wouldn't work, because they'd all be referring to different WebSocket connections.
//This is what I was referring to when I mentioned that the two-way WebSocket example was "messier".
//This global stuff isn't needed to simply listen on a WebSocket, as we'll see later with the Pusher-based 2nd example app.
window.WEB_SOCKET = () => {
return webSocket
};
//This is our response message queue. I tried building this initially with a Temporary State object, but
//I ran into some weird issues arouund live-updating that I suspect have something to do with React's internal visual cycles.
let messages = [];
//`window` properties in Retool must be functions to be able to access them as globals from within any query.
//For example, when you see a line that looks like:
//MESSAGES().push(...) it's really referring to our global response messages queue
window.MESSAGES = () => {
return messages;
};
//I originally set this up as a simple `clear` parameter for the above function, but the global properties don't support function
//arguments. I suspect this is some weird Javascript sandbox behavior, but either way, I can confirm that this works.
window.CLEAR_MESSAGES = () => {
messages = []
};
const webSocket = WEB_SOCKET() //grab a reference to the global WebSocket object mentioned earlier
//This allows us to run a function every time the WebSocket receives a message.
//In this example, this happens any time we send a message because the echo WebSocket sends our message back to us.
webSocket.addEventListener('message', function(event) {
//This gets a reference to the global response messages list (array) and appends the response that comes back from the echo WebSocket
MESSAGES().push(event.data)
//This is a retool hacky thing to ensure the text actually visually updates its contents.
text1.setValue(MESSAGES().join('\n'))
});
const webSocket = WEB_SOCKET()
//This line specifically is what would fail if we didn't have a reference to the same `WebSocket` object.
//If we created a new `WebSocket` object in this query, we would be sending messages to THAT `WebSocket` instead of the global one
//we created earlier. Then, the echo service would be echoing to THAT `WebSocket` instead of the one we are actually listening to.
//Basically we do the global stuff just to be able to do this `webSocket.send(...)` call.
webSocket.send(textInput1.value)
//The part that actually clears the messages queue by just re-assigning an empty array
CLEAR_MESSAGES()
//The part that updates the textbox visually because Retool
text1.setValue(MESSAGES().join('\n'))
const pusher = new Pusher('app-key-goes-here', {
cluster: 'us2'
});
//This is where Pusher sets up the WebSocket in the background and subscribes to the Pusher channel called `event-channel-goes-here`
//By itself, this doesn't listen to any events on this channel. You need the `channel.bind(...)` line below.
const channel = pusher.subscribe('event-channel-goes-here');
//This is the exact same thing as an EventListener. The syntax is even similar by design so that it looks familiar.
//Inside the `function(data) { ... }`, you can do any Retool things that you would like to do in response to Pusher events.
//In this example, I am triggering a simple query, which will refresh the data inside a table.
channel.bind('event-type-goes-here', function(data) {
get_table_data.trigger()
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment