Reusable websocket component
Hi Community and @manniL / TheAlexLichter
i found the super helpfull small guide on YT:
https://www.youtube.com/watch?v=OfY7JcrqkPg&t=33s
It works like a charm! Now I'm wondering how I can create a reusable component for sending messages.
The idea is to have a central "channel" for updates across the web app. For example, I've written a mail client, and when this client receives a new email, it should send a message to the channel. Based on this message, a component refresh should be triggered.
But I'm having trouble implementing this. The main idea is to have a function that I can import anywhere on the server side to send messages to this channel.
Alexander Lichter
YouTube
Integrating WebSockets in Nuxt and Nitro
🎉 Nitro 2.9 was released just before Vue.js Amsterdam and brings new features such as a database layer, a task API and also WebSocket support! But how can we integrate it in a Nuxt application? This video will teach you 👌
Key points:
🛠 How to set up WebSockets in Nitro and in Nuxt
💡 Working from scratch to a functional application
⚠️ Hints to m...
28 Replies
Hey! Do you mean a Vue component or a server utility function?
Your description has parts of both
I meant a server utility function. sorry for not being that precise! 🙂 On the frontend, it's clear that I can consume these events using
useWebsockets
.
But on the server side, I need a function that allows me to send events.
for example:
So, I can't figure out how to create the sendWebSocketMessage function.How about Nitro Docs » Guide » WebSocket » Usage?
https://nitro.unjs.io/guide/websocket#usage
Extract the logic into services and it is lean and readable. Why would you abstract it more?
For centralized logic you should rather use KV subscription or dedicated message bus.
open()
definition would need to include subscription to external source
And the sendWebSocetMessage
you drafted, would post message to the MQ (or KV). From the comments I guess it should include client ID so you propagate event to the correct user.This is exactly where I’m struggling...
I’m not sure how to properly extract this so that it works in the end.
What do you mean by KV or a dedicated message bus? can you send me an example?
If by "dedicated message bus" you mean a topic where the "clients" (in this case, the frontend) subscribe, then yes, that's exactly what I'm trying to do. The example above is just a quick and dirty implementation to see if it would work in my existing project—and yes, it does! Now, I’m at the stage where I want to implement the WebSockets more cleanly.
The plan was to use WebSockets for this since a chat feature will also be added later.
Maybe the better question is: how do I send a message?
I can import the message from
I can import the message from
server\api\websocket.ts
, but how do I send it to all peers?With
peer.publish(...)
, but I guess you will rather post to the subset of users that joined a named room. This is why you should rather utilize peer.send(...)
or figure out some other way.
See example public chat
I think it would be cleaner to rename peer
to ctx
or something.. but do what you want 😉I think we're not talking about the same thing! 🙂 I adapted the example from here:
https://stackblitz.com/edit/nitro-web-sockets-fmeygr?file=server%2Fservice%2Fmail%2FparseEmail.ts I hope this example makes things clearer! I know how to send and react to messages inside
https://stackblitz.com/edit/nitro-web-sockets-fmeygr?file=server%2Fservice%2Fmail%2FparseEmail.ts I hope this example makes things clearer! I know how to send and react to messages inside
_ws.ts
, but I’m unsure how to reuse this to send a message from another component to the same "room."
I created service/mail/parseEmail.ts
to clarify what I want to do. As a test, I simply created a function that logs a message every 10 seconds, and // message('newMail') ???
is a placeholder where it should send something to all peers.
Hopefully, this makes the problem clearer! It would be awesome if you could replace the placeholder with something that works 😄Hmm, you’re using a temporary store and all consumers will read from there. Isn’t that kind of an ugly solution? Then I’d also need to delete the entries from time to time, and the frontend would need extra logic to handle that too.
I did something similar in the past with Socket.io, and I was hoping to use a built-in feature in Nuxt this time. If I can’t figure out how to do this, I’ll need to install the Socket.io package again.
What's ugly about it? in-memory KV acts like a queue, you can clear it anytime (even right before when you set the value). The system is then decoupled and queue technology can be swapped (from in-memory to something cloud-based). You can wrap set/watch/clear methods with a custom service API. The only alternative would be to share/leak
peer
instance on open so some service could call .publish(...)
anytime it thinks appropriate, while in real-world scenario you'd end up with an array of peer
s for all open connections and utilizing .send(...)
instead to avoid broadcasting to all chat rooms.
Ofc built-in solution would be preferrable, however it is always a trade - flexibility vs convenience 😉
IMO, frontend should remain dumb and avoid having any logic to "handle" broadcast messages.With the alternative using sockets, for example, I don’t need to worry about cleaning up entries, and I also don’t really need to care about who is connected and who isn’t (which I think is similar to KV). I can simply send a message to consumers, and it’s not permanent, meaning I don’t have to clean it up after sending or setting something.
On the other hand socket.io is an estabilished library and should be a good fit for most cases. Hopefully it won't be a large overhead on top of Nitro.
ChatGPT is suggesting using WebSockets or SSE, but I’ve never used SSE before.
The 2nd Deno link used SSE.
Dude.. don't listen to mr-generate-random-plausible-answer-from-noise. Use it for generating tests or something, but don't ask it for tech-related stuff, because it will spit lies from training data and you will never know how much truth is there until you dig the topic yourself.
Sure, but it gives me ideas, and after a bit of research, I can decide which way I want to go.
So you said you worry about
.clear()
before .set()
. I wouldn't. It means you use storage as signal bus. It does not matter if it contains an item or not.
The only problem is that it is not a solution that clearly speaks "I am dedicated for chat app".
If you think socket.io has dedicated methods that would make your source code more clearly state the intent (readable, maintainable), then I see no reason to seek other solutions 🙂If I take a look at this guide:
https://socket.io/how-to/use-with-nuxt It looks like it's exactly what I was searching for. I just need to trigger
https://socket.io/how-to/use-with-nuxt It looks like it's exactly what I was searching for. I just need to trigger
socket.emit("blabla")
.How to use with Nuxt | Socket.IO
This guide shows how to use Socket.IO within a Nuxt application.
If you can use it to replace unstorage from the last demo on Stackblitz, I would gladly fork it for later 😉
If I read the page correctly,
socket
lives on the client, so emit
just sends message from the browser to the server. I am curious if you can figure out how to utilize it on the backend.I’m also a bit puzzled when I read this. It should actually be global according to how it’s used, but I need to test it. It’s clearly stated here that it’s global:
https://nuxt-socket-io.netlify.app/usage/
It refers to
nuxtServerInit
from Nuxt 2. Regardless, if you decide to use some "magic" global, beware it won't scale. You can only broadcast to people connected to specific backend instance.Okay, I figured it out... my issue is solved. I created a utility with this content:
Additionally, I use a plugin to open one connection, and after that, I can use wsSend everywhere on the server side. F***ing awesome!
If I read it correctly... anytime you open a new connection you close previous one. So you actually support single user for the whole backend instance. Is it meant to run on edge and spin up worker for every user?
Oh.. I may have misunderstood the whole idea. You want to send from client to backend, not the other way around.
Well, this is only a partial solution.
In the plugin, I open the connection with
In the plugin, I open the connection with
wsConnect()
, and in my services on the server side, I use the connection with wsSendOnce("bla bla bla")
.Is the browser involved in it? or is it service-to-service?
Because you won't open ws connection straight to client browser - he does not have any URL to point to.
No, no, you got me completely right... I couldn't figure out how to reuse the connection from
server\api\websocket.ts
, so I just created a new "client" to send the messages.
In a later stage, server\api\websocket.ts
will be secured so that only the server can send messages.
The browser will also connect to server\api\websocket.ts
, but it will only be the consumer of the messages in the end.
And this will be the baseline for my reactivity—when something changes in the backend, I'll notify all consumers to update via a regular API call.
And of course, the name and path of server\api\websocket.ts
will also be changed! 🙂in ultra short this is the principle