Bun is not a drop-in replacement for Node yet since many Node-specific APIs are still not implemented. It is compatible with NPM, which is 17x faster than pnpm, 29x faster than npm and 33x faster than yarn. Performance-wise, Bun seems to dominate the existing ecosystems.
In terms of websockets, Bun uses μWebSokets implementation, which is blazingly fast since it's written in C and C++. It's a bummer that Bun only provides a Bun-specific Pub/sub API for its websockets. So, let's build our own Pub/Sub protocol on top of Bun's WebSocket.
The Fun Part
First of all, we need to have Bun installed on your system. Once that's taken care of, create a new project with the command
bun init. It will ask you a few questions; just answer them as you like. After that, open the newly created project in your favourite code editor.
Bun provides a global variable
Bun to interact with native Bun APIs. In our case, we will use the
serve method to create a fast HTTP web server. It takes in an options parameter, which you can pass various parameters. We are primarily concerned with the
websocket parameters. Bun allows you to upgrade your HTTP connection to a websocket, but you'll need to write your own upgrade logic, which is done inside the
fetch function. The
serve method also accepts an optional generic argument for the
data property, which acts as the context for the websocket instance. The
data property will be available for each websocket instance.
The heart and core of our pub/sub web server will be our
PubSubManager class. It will manage subscribing to channels, publishing messages to channels, etc.
Let us break down what's happening in the code. The
PubSubManager class keeps track of all the channels, their subscribers and message queues in memory (for the sake of simplicity, this can be moved to external services such as Redis). This class can also be used in other runtimes (NodeJs, Deno). The
subscribe function here will add a socket to the list of subscribers of a channel. The
unsubscribe function does the opposite. The
publish function adds the message to the message queue of the provided channel. The
onConnectionClose method is there to handle the websocket disconnect.
The main thing to notice here is the broker function. This function runs periodically; in the example, it's called every 50ms. On every run, it serially checks for the messages in all the channels and, if present, broadcasts them and clears the message queue. It is somewhat similar to the event loop. This is done here for simplicity; we can use RxJS and observables to make this more performant.
Now, let's actually use the
PubSubManager in our Bun web server. In the
index.ts file, populate the following:
Here, our server is running at
0.0.0.0 and port
1337. In the
fetch property, we are upgrading the connection from HTTP to WebSockets. Also, we are adding additional data to the socket context: the
socket id (whose value is a random UUID) and the date and time of socket connection establishment. This data is available anywhere in the socket instance. We can also handle request validation and auth handling in this function.
Let's start our API server with the following command in the project root:
Now, let us check out our server in action. Open Postman/Insomnia or any of your favourite API testing clients and open a websocket connection to
Voilà! We have established a websocket connection to our server.
Now let's connect to the channel "channel1". Let's send the following JSON data
We can see that we are subscribed to "channel1".
Now let us try sending messages to "channel1". Send the following JSON
Just as we hit send, we will receive the message from channel1 that we have subscribed to.
We learnt how to implement a pub/sub websocket server in Bun. Since Bun is still in its early stages, there is much more to learn. We shall cover them in future blogs.
Thank you for reading this article. Please consider leaving a comment below if you liked it.