Building Scalable WebSocket APIs with Socket.IO Syntax

If you've got any questions feel free to DM on Twitter @reillyjodonnell
Search for a command to run...

If you've got any questions feel free to DM on Twitter @reillyjodonnell
No comments yet. Be the first to comment.
Someone shared this image above. At first glance, I wasn’t sure about the perf implications — so let’s break it down from first principles :D Let’s deconstruct this monster Here’s the code: // Function to flatten React Context Providers. const flatte...

Intro Hello it’s me from the future! I originally was going to go over file based routing but pivoted to going over important SSR concepts with React / how Bun makes it easy. It’s full of struggles with hydration (mismatches), React entry points, and...

history? Serverless is EVERYWHERE and for good reason: nearly infinite scalability, physically closer to users, and pay-for-what-you-use pricing. But not without tradeoffs — vendor lock-in, (potentially) higher costs, and the infamous cold-starts. cf...

Powered by bash and AppleScript!

Sockets can only transmit binary (text.)
Imagine we have this data
const message = {id: '123', name: 'Reilly', message: 'Hey'}
Here's the problem: we want to send this data to the ui but we have hundreds of useEffects spread throughout the codebase. How can we make sure that this message is processed in the right part of the codebase and not somewhere else?
The best solution I've seen is to use a unique identifier - like a title.
This is what Socket.IO does.
Here's how we could implement their API from scratch:
function ws(url: string) {
const socket = new WebSocket(url);
// This looks scary
// It's just a way to manage subscriptions
// The key is the thing we are subbing to
// the value is an array of functions to call when we get a message
const subscriptions: { [key: string]: ((content: any) => void)[] } = {};
// Connection opened
socket.addEventListener('open', (event) => {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', (event) => {
try {
const message = JSON.parse(event.data);
if (!message.type || !message.content) return;
const { type, content } = message;
if (!subscriptions[message.type]) return;
if (subscriptions[type]) {
subscriptions[type].forEach((fn) => fn(content));
}
} catch (error) {
console.error('Error parsing message', error);
}
});
return {
on: (type: string, fn: (content: any) => void) => {
if (!subscriptions[type]) {
subscriptions[type] = [];
}
subscriptions[type].push(fn);
},
};
}
1. We start with the return part - we know we want the person to say socket.on(...) so we need to return the on property.
2. They provide a string to "sub" them to the message they care about. They give us a function and we call that function with their data when that message comes in.
Now we just need a mechanism to keep track of the messages they've subbed to.
3. We implement an object where the keys are the names of the messages and the values are an array of the functions they provide.
4. Call the functions when the messages come in.
And here's how we can call it:
const socket = ws('ws://localhost:3000/ws')
socket.on('test', (data) => {
console.log(data);
})
Now your turn! How would you implement the server part so that we could say
socket.emit('message', {id: 0, name: "Reilly", message: 'hi'})
Thanks for reading <3