W
Wasp•4mo ago
dbalbo

websockets - emit event from server

Hi guys! I integrated websockets following the guide at https://wasp-lang.dev/docs/advanced/web-sockets. It's not clear to me how to do an emit in an operation (therefore on the server side). For example, at the end of writing a record to the DB, I would like to perform an emit. Thank you
12 Replies
kapa.ai
kapa.ai•4mo ago
Hi @dbalbo. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To emit an event from the server side in Wasp, you would typically use the io object within the webSocketFn function. However, currently, Wasp doesn't provide a built-in way to access the io instance outside of the webSocketFn function. A workaround for this issue has been discussed in the Wasp community. Here's the suggested approach: 1. In the file where you have your webSocketFn function, create a "global" variable that you export from that file. Let's call it let ioInstance = undefined. 2. Then set that ioInstance to the io instance you have inside of the webSocketFn function. 3. Elsewhere in your codebase, import the ioInstance and use it. Here's an example of how you might do this:
// In your webSocket.js file
let ioInstance = undefined;

export const webSocketFn = (io, context) => {
io.on('connection', (socket) => {
// Your code here...
})
ioInstance = io;
}

export const getIoInstance = () => ioInstance;
// In your webSocket.js file
let ioInstance = undefined;

export const webSocketFn = (io, context) => {
io.on('connection', (socket) => {
// Your code here...
})
ioInstance = io;
}

export const getIoInstance = () => ioInstance;
Then, in your operation, you can import getIoInstance and use it to emit events:
import { getIoInstance } from './webSocket';

// In your operation...
const io = getIoInstance();
io.emit('yourEvent', yourData);
import { getIoInstance } from './webSocket';

// In your operation...
const io = getIoInstance();
io.emit('yourEvent', yourData);
Please note that this is a workaround and not an official solution. The Wasp team is aware of this limitation and is working on a more integrated solution (source (https://github.com/wasp-lang/wasp/issues/1289)). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @dbalbo, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
miho
miho•4mo ago
The bot gave a good answer. We need to improve this API, but the workaround should work fine. Did you try it?
fustuk.
fustuk.•3mo ago
it does, but the docs and maybe the config around sockets need to be updated in the future
miho
miho•3mo ago
Yep yep, I agree 100%. There should be something like import { getIoInstance } from 'wasp/server/websocket' and you'd just use that 🙂 + we should also improve the existing docs to mention the workaround!
miho
miho•3mo ago
GitHub
Enable using the Websocket server inside of server-side code outsid...
Right now, the initialized server is not exposed or documented for the user to use. For example, when somebody likes a post, it might trigger something and a notification is sent over the socket.
marcoeg
marcoeg•2mo ago
This is very helpful but I still cannot figure out how to send an event to a specific listener. The pattern I would like to implement is very similar, but I would like a client to be able to subscribe to events for the users that is logged in in that client. socket.js:
let ioUserInstance = [];

export const socketFn = (io, context) => {
io.on('connection', (socket) => {
if (Object.keys(socket.data).length !== 0) {
console.log('user connected: ', socket.data.user.id)
ioUserInstance[socket.data.user.id] = io
}
})

io.on('disconnect', () => {
console.log('Client disconnected');
});
};

export const getIoUserInstance = () => ioUserInstance;
let ioUserInstance = [];

export const socketFn = (io, context) => {
io.on('connection', (socket) => {
if (Object.keys(socket.data).length !== 0) {
console.log('user connected: ', socket.data.user.id)
ioUserInstance[socket.data.user.id] = io
}
})

io.on('disconnect', () => {
console.log('Client disconnected');
});
};

export const getIoUserInstance = () => ioUserInstance;
API code:
import { getIoUserInstance } from '../socket.js';

...
const ioUser = getIoUserInstance();
const userId = location.userId;

ioUser[userId].emit('newIncident', newIncident);
import { getIoUserInstance } from '../socket.js';

...
const ioUser = getIoUserInstance();
const userId = location.userId;

ioUser[userId].emit('newIncident', newIncident);
All connected clients receive the event.
miho
miho•2mo ago
Can you try doing this: ioUserInstance[socket.data.user.id] = socket? io is everyone, socket is just that user
marcoeg
marcoeg•2mo ago
Yes, that is the correct way to do it. Assigning socket works. Thank you!
marcoeg
marcoeg•2mo ago
I have a problem I can't make a reason of. When using the front-end from a machine that is not the same of wasp application, accessing for instance http://192.168.50.71:3000 instead of http://localhost:3000, the client is still trying to open the websocket at localhost:3000 and failing. Any suggestions? client code:
import { useState } from 'react'
import { SelectChangeEvent } from '@mui/material'
import { useQuery, getAllIncidents } from 'wasp/client/operations'
import { useSocketListener, useSocket } from 'wasp/client/webSocket'
import { IncidentTable, PageWrapper } from '../components/organisms'
import { Select } from '../components/molecules'

const FETCH_ITEMS_COUNT_OPTIONS = ['10', '25', '50', '100'] as const
type ItemsCount = (typeof FETCH_ITEMS_COUNT_OPTIONS)[number]

const DashboardPage = () => {
const [fetchItemCount, setFetchItemCount] = useState<ItemsCount>('10')
const {
data: incidents,
isLoading,
error,
refetch,
} = useQuery(getAllIncidents, { numberOfItems: parseInt(fetchItemCount) })

const { socket, isConnected } = useSocket()
console.log('isConnected:', isConnected)
// Use useSocketListener
useSocketListener('newIncident', (data: any) => {
console.log('New incident received:', data)
refetch()
})
import { useState } from 'react'
import { SelectChangeEvent } from '@mui/material'
import { useQuery, getAllIncidents } from 'wasp/client/operations'
import { useSocketListener, useSocket } from 'wasp/client/webSocket'
import { IncidentTable, PageWrapper } from '../components/organisms'
import { Select } from '../components/molecules'

const FETCH_ITEMS_COUNT_OPTIONS = ['10', '25', '50', '100'] as const
type ItemsCount = (typeof FETCH_ITEMS_COUNT_OPTIONS)[number]

const DashboardPage = () => {
const [fetchItemCount, setFetchItemCount] = useState<ItemsCount>('10')
const {
data: incidents,
isLoading,
error,
refetch,
} = useQuery(getAllIncidents, { numberOfItems: parseInt(fetchItemCount) })

const { socket, isConnected } = useSocket()
console.log('isConnected:', isConnected)
// Use useSocketListener
useSocketListener('newIncident', (data: any) => {
console.log('New incident received:', data)
refetch()
})
No description
No description
marcoeg
marcoeg•2mo ago
trying with setting up WASP_SERVER_URL
MEE6
MEE6•2mo ago
Wohooo @marcoeg, you just became a Waspeteer level 1!
marcoeg
marcoeg•2mo ago
even with WASP_SERVER_URL set in .env.server Wasp is attempting to establish a WebSocket connection to localhost. after setting REACT_APP_API_URL in .env.client it is now attempting to open the WebSocket from the right server. Failing for CORS issues now but should be solved with proper middleware setup.
Want results from more Discord servers?
Add your server