8

I have an Express server that listens for webhook events from an external API. When it received these events, I want the handler for that http request to send a message to a WebSocket client. Here is some basic code to illustrate what I mean

The external API will send a HTTP POST request to an endpoint on my Express server, lets say it looks like this:

app.post('/external-api', (req, res) => {

    // webhook payload will be in req.body
})

In that handler for /external-api I'd like to send a message to a client who's connected to the server via WebSocket. As of right now, I'm using the npm ws library, so I want the logic to look something like this

app.post('/external-api', (req, res) => {

    ws.broadcast(req.body);
})

Is there a way to do this? I'm open to using other libraries but I just need a way to send a message to a WebSocket client from a HTTP POST request handler in Express.

Gautam Jethwani
  • 121
  • 1
  • 2
  • 10

1 Answers1

11

Here is an example:

index.ts:

import express from 'express';
import WebSocket from 'ws';
import http from 'http';
import path from 'path';
import faker from 'faker';

const app = express();
const port = 3000;
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

wss.on('connection', (ws: WebSocket) => {
  console.log('establish websocket connection');
  ws.on('message', (message) => {
    console.log('received: %s', message);
  });
});

app.get('/client/:id', (req, res) => {
  res.sendFile(path.resolve(__dirname, `./public/client-${req.params.id}.html`));
});

app.get('/external-api', (req, res) => {
  wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(faker.internet.email());
    }
  });
  res.sendStatus(200);
});

server.listen(port, () => console.log(`http server is listening on http://localhost:${port}`));

client-1.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>client 1</title>
  </head>
  <body>
    <p>client 1</p>
    <button id="test">Test</button>
    <script>
      (function() {
        window.onload = function() {
          const socket = new WebSocket('ws://localhost:3000');

          // Connection opened
          socket.addEventListener('open', function(event) {
            socket.send('Hello Server! Client - 1');
          });

          // Listen for messages
          socket.addEventListener('message', function(event) {
            console.log('Message from server ', event.data);
          });

          const btn = document.getElementById('test');
          btn.addEventListener('click', async () => {
            try {
              const res = await fetch('http://localhost:3000/external-api');
              console.log(res);
            } catch (error) {
              console.log(error);
            }
          });
        };
      })();
    </script>
  </body>
</html>

client-2.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>client 2</title>
  </head>
  <body>
    <p>client 2</p>
    <button id="test">Test</button>
    <script>
      (function() {
        window.onload = function() {
          const socket = new WebSocket('ws://localhost:3000');

          // Connection opened
          socket.addEventListener('open', function(event) {
            socket.send('Hello Server! Client - 2');
          });

          // Listen for messages
          socket.addEventListener('message', function(event) {
            console.log('Message from server ', event.data);
          });

          const btn = document.getElementById('test');
          btn.addEventListener('click', async () => {
            try {
              const res = await fetch('http://localhost:3000/external-api');
              console.log(res);
            } catch (error) {
              console.log(error);
            }
          });
        };
      })();
    </script>
  </body>
</html>

Now, click the button of client 1, send HTTP request to /external-api.

The console logs of client 1:

enter image description here

The console logs of client 2:

enter image description here

The logs of server:

http server is listening on http://localhost:3000
establish websocket connection
received: Hello Server! Client - 1
establish websocket connection
received: Hello Server! Client - 2

As you can see, the server broadcast fake emails to client 1 and client 2.

Lin Du
  • 88,126
  • 95
  • 281
  • 483