DEV Community

Kailash Sankar
Kailash Sankar

Posted on • Edited on

Websockets with React & Express [Part-1]

A quick tutorial on setting up websockets on a react + express app. This tutorial assumes you already have a running react client and express server.

If you are new to websockets and would like to know how it works, I recommend reading this post - Websockets 101

WebSocket server

We'll be using ws package to setup the websocket server. First let's write a websocket setup function.

// setupWebSocket.js
const WebSocket = require("ws");

// accepts an http server (covered later)
function setupWebSocket(server) {
  // ws instance
  const wss = new WebSocket.Server({ noServer: true });

  // handle upgrade of the request
  server.on("upgrade", function upgrade(request, socket, head) {
    try {
       // authentication and some other steps will come here
       // we can choose whether to upgrade or not

       wss.handleUpgrade(request, socket, head, function done(ws) {
        wss.emit("connection", ws, request);
       });
    } catch (err) {
      console.log("upgrade exception", err);
      socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
      socket.destroy();
      return;
    }
  });

  // what to do after a connection is established
  wss.on("connection", (ctx) => {
    // print number of active connections
    console.log("connected", wss.clients.size);

    // handle message events
    // receive a message and echo it back
    ctx.on("message", (message) => {
      console.log(`Received message => ${message}`);
      ctx.send(`you said ${message}`);
    });

    // handle close event
    ctx.on("close", () => {
      console.log("closed", wss.clients.size);
    });

    // sent a message that we're good to proceed
    ctx.send("connection established.");
  });
}
Enter fullscreen mode Exit fullscreen mode

Next we tie our setup function with our express server,

// app.js or bin/www, where you currently have express setup

// app should be your express app
const server = http.createServer(app);

// pass the same server to our websocket setup function
// the websocket server will the run on the same port
// accepting ws:// connections
setupWebSocket(server);
Enter fullscreen mode Exit fullscreen mode

Time to test, I use a firefox plugin called Weasel to connect to socket and test, there are similar alternatives in chrome.

Try connecting to the socket and also check the server logs to see the output of console.log statements.

// connect to your express server host and port but using ws protocol
// for example
ws://localhost:3000
Enter fullscreen mode Exit fullscreen mode

Let's add some more functionality to our server to check if individual client messages and broadcast messages are working fine.

Create the functions below in another file,

// pipeline.js, for some examples

// client specific messages
// each client gets an individual instance
function individualPipeline(ctx) {
  let idx = 0;
  const interval = setInterval(() => {
    ctx.send(`ping pong ${idx}`);
    idx++;
  }, 5000);
  return interval;
}

// braodcast messages
// one instance for all clients
function broadcastPipeline(clients) {
  let idx = 0;
  const interval = setInterval(() => {
    for (let c of clients.values()) {
      c.send(`broadcast message ${idx}`);
    }
    idx++;
  }, 3000);
  return interval;
}
Enter fullscreen mode Exit fullscreen mode

Import these functions in to our setup file,

// setupWebSocket.js

function setupWebSocket(server) {
  // ws instance
  const wss = new WebSocket.Server({ noServer: true });

 // hookup broadcast pipeline
 broadcastPipeline(wss.clients);

 wss.on("connection", (ctx) => {
  /* ... */
  const interval = individualPipeline(ctx);
  /* ... */
  // clear the interval on connection close event
  ctx.on("close", () => {
   console.log("closed", wss.clients.size);
   clearInterval(interval);
  });
 }
Enter fullscreen mode Exit fullscreen mode

Now use Weasel (or equivalent) and watch ping pong and broadcast messages. Connect from multiple tabs, and observe broadcast message appearing in sync in all tabs, while ping pong is specific to each client.

broadcast message 5
ping pong 0
broadcast message 4
Enter fullscreen mode Exit fullscreen mode

I'll leave the link to client and server code on the final part as we are going to add more functionality to this.

We'll work on client side in Part-2.

Top comments (7)

Collapse
 
dmikester1 profile image
Mike Dodge

For some reason the test connection in Weasel is not working for me. I keep getting "The Connection is Abruptly Closed or Couldn't be Opened ! CONNECTION CLOSED!" Does the URL and the protocol need to have WS in it? Or just the URL? Right now I have URL: ws://localhost:3001 and protocol: ws.

Collapse
 
ksankar profile image
Kailash Sankar

Protocol is optional, it's only needed if you are taking decisions on the server based on it's value.

Can you try connecting to wss://echo.websocket.org, it's an echo server and would return the messages posted to it. If this works, then the issue is on your localhost websocket server setup.
websocket.org/echo.html

Collapse
 
dmikester1 profile image
Mike Dodge

Yeah, connecting to that URL works fine. I'll wait for the fifth article and compare my code to your's.

Thread Thread
 
dmikester1 profile image
Mike Dodge • Edited

Maybe you can see something wrong. This is main file that kicks off the server. dev-to-uploads.s3.amazonaws.com/i/...

Thread Thread
 
ksankar profile image
Kailash Sankar

I didn't get it at first, but after staring at it for a while I think the reason is using
app.listen(port) instead of server.listen(port);
Never realised before but found a good post about it at StackOverflow

I still have some cleaning up to do on the code, here's what I've done till now on server

Thread Thread
 
dmikester1 profile image
Mike Dodge

That was definitely the issue! Thank you so much!

Collapse
 
ismailarilik profile image
İsmail Arılık

This server does not respond. FYI