Show HN: Socket-call – Call socket.io events like normal JavaScript functions
36 bperel 16 6/16/2025, 11:40:54 AM github.com ↗
Hello HN,
I built a Typescript library (named socket-call, for lack of a more sexy name) whose goal is to be able to call socket.io events as regular functions.
So you declare your server-side like so:
...
const listenEvents = (services: UserServices) => ({
// Add your events here, the name of the event is the name of the function
login: async (username: string) => {
services._socket.data.user = { username };
console.log(`User ${username} logged in`);
setInterval(() => {
// Calling an event that's handled client-side
services.showServerMessage(`You're still logged in ${username}!`)
}, 1000);
return `You are now logged in ${username}!`;
},
});
and then on the client side you call them like normal async Javascript functions (and you can also create client-side event handlers): ...
const user = socket.addNamespace<UserEmitEvents, UserListenEvents>(
'/user'
);
// Calling an event that's declared server-side
user.login(username.value).then((message) => {
console.log('Server acked with', message);
});
// Handling an event that is sent by the server
user.showServerMessage = (message) => {
console.log('Server sent us the message', message);
}
I use this library for my own projects and would be interested to receive feedback about it :-)
I did something similar using VSCode's core ipc / rpc which only requires a transport (protocol) to implement {send, onMessage}. I use it in a Chrome extension so I have to implement my own socket.io and port message passing protocols. Some of the benefits are being able to send a message from MAIN world of an injected content script (if you want to intercept all fetch and XMLHttpRequest requests, for example) through a tunnel in the isolated world content script to the side panel which could theoretically tunnel it to a server over socket.io. If I have a Math service, for example, that only adds two numbers, it can be called from anywhere in the system with `await mathService.add(1,1);` with mathServer being dependency injected using constructor(@IMathService private readonly mathService: IMathService). This is how VSCode manages calling code across hundreds of different isolated JavaScript runtime environments.
What I did was a bit overkill and likely trpc would have been good enough if I knew about it when I started.
[0] https://github.com/bperel/socket-call/blob/e0076d7887397a92a...
[1] https://github.com/microsoft/vscode/blob/24c0ff16c250f2b39ee...
In fact, websockets work so well I use them as a generic TCP replacement, because the message oriented transport model gives me 99% of what I need with the exception of custom message types. Leaving that out was a massive letdown to me, because you now need to carry a way to identify the message type inside the body, rather than just throwing the message itself into the appropriate protocol parser (e.g. a schema based binary format).
1. Connectivity. The WebSocket connection is only as persistent as the underlying network connection between the client and the server. A person playing a web-based game on a mobile device on a train that then goes under a tunnel is a good example.
WebSockets do not reconnect if they close unexpectedly. In such cases, you have to throw the WebSocket instance away and create a new one, and so you end up having to implement your own reconnectivity logic.
2. Message Sending. Messages will only be sent if the connection is open. If it is closed, not only do the messages not get sent, but they don't get queued up either, so they end up disappearing into the ether.
If you want to guarantee message sending, then you end up having to implement a queuing mechanism that is linked to knowing the status of the WebSocket connection, and is able to send when the conditions are right.
3. If you don't use WSS (WebSocket Secure Server) for the WebSocket host and connection url, then the WebSocket connections can get interfered with if they are connecting over a mobile network - ISPs sometimes inject packets which ends up distorting WebSocket connections over http. But I think since the days of Ed Snowden's leaks everyone has their production WebSocket systems setup using WSS.
This comes from the experience many years ago of working on a WebSocket-powered web framework called SocketStream which ran into these issues, and then some years ago I managed to build a library that focussed on dealing with those WebSocket-related issues, called Sarus: https://github.com/anephenix/sarus
WebSockets is great though, and there is still much that can be done with it as this library in the HN post demonstrates.
It is not unnecessary, and you probably could build something that does the same things in a day or two, but I'd be surprised if it was something that scales as well to >100,000 simultaneously connected people
It doesn't look difficult at all now I look at it https://javascript.info/websocket
Eh... While I agree that socket.io is one of those libraries you could probably "write" in an afternoon, and Websockets are simple, there are a couple of things that are kinda painful to rewrite time after time:
I've written countless WebSocket wrappers in the past (similar aversion to socket.io as others in this thread). The one-shot output was perfect. Certainly better than my patience would've allowed.
Maybe socket.io is doing something fancy on the server side, but for clients, it's absolutely overkill.
[0] https://docs.convex.dev/quickstart/script-tag