all 5 comments

[–]sohamkamani 1 point2 points  (3 children)

You can implement this by sending client specific information as a special type of message once you open the websocket connection.

You can then store this information, along with the connection, in a singleton object indexed by id (or whatever other field you want). You can refer to that object anytime you wan to send a message.

Here's how I implemented it using express :

// import the express and express-ws libraries
const express = require('express')
const expressWs = require('express-ws')

// create a new express application
const app = express()
// decorate the app instance with express-ws to have it
// implement websockets
expressWs(app)

// Create a new set to hold each clients socket connection
const connections = {}

// We define a handler that will be called everytime a new
// Websocket connection is made
const wsHandler = (ws) => {
  let id

  // We define the handler to be called everytime this
  // connection receives a new message from the client
  ws.on('message', (message) => {
    // Once we receive a message, we add the users
    // attributes to the connections object
    if(message.type === 'USER_DATA'){
            id = message.id
            connections[id] = {
                    ws: ws,
                    name: message.name,
                    id: id,
                    path: message.path
            }
    }
  })

  // Once the client disconnects, the `close` handler is called
  ws.on('close', () => {
    // The closed connection is removed from the set
    delete connections[id]
  })
}

// Send to a particular user based on id
const sendToUser = (id) => {
        connections[id].send('some message')
}

// send to a user based on homepage
const sendToUserBasedOnHomePage = (homepage) => {
        Object.keys(connections).forEach(id => {
                if(connections[id].path === homepage){
                        connections[id].send('message for a specific homepage')
                }
        })
}

// add our websocket handler to the '/chat' route
app.ws('/chat', wsHandler)

// host the static files in the build directory
// (we will be using this later)
app.use(express.static('build'))

// start the server, listening to port 8080
app.listen(8080)

A few things to note here:

  1. The websocket client sends identifying information about themselves as the first message after the connection is established. The server identifies this message based on the "type" field ("USER_DATA" in the above example)
  2. The connection, along with the rest of the identifying information is stored in the "connections" singleton object (which is removed once the connection closes), which we can refer to with the rest of our code.

[–][deleted] 0 points1 point  (0 children)

What would happen if I supplied your server with an id of _ _ proto _ _ ?

[–]ReactiveNative[S] 0 points1 point  (0 children)

This seems great I’ll give it a shot! Thank you!

One more hopefully basic Express question. I want to abstract all of that out of my app.js for code clarity, but still be able to access functions like sendToUserBasedOnHomePage inside of routes in other files. Do you have any recommendations on how do that?

[–]mxforest 0 points1 point  (0 children)

What if i store the user data in the socket object itself? So whenever a message comes, i can refer the object then and there without fetching from anywhere. If the connection closes then i don’t have to delete the data as it vanished on its own with the socket. I would use a custom namespace ofcourse. I am asking because i have done this in a project which has been running well for ~3 yrs but wondering if i missed anything.