Client to retrieve data from backend server database table via a websocket connection
Asked Answered
C

2

2

I am using the following server code to retrieve data from a postgres db:

    const express = require('express')
    const app = express()
    const server = require('http').createServer(app);
    const pool = require("postgresql");
    const WebSocket = require('ws');
    
    const wss = new WebSocket.Server({ server:server });
    
const getTempData = async () => {
  try {
    const tempData = await pool.query("select country, temp from my_temp_table");
    return JSON.stringify(tempData.rows)
  } catch(err) {
      console.error(err.messasge);
  }
}

wss.on('connection', async (webSocketClient) => {
  console.log('A new client Connected!');   
  const tempDetails = await getTempData();
  
  webSocketClient.send(tempDetails);      
  webSocketClient.on('message', (message) => {
    console.log('received: %s', message);    
  });
});           
server.listen(3000, () => console.log(`Listening on port :3000`))

Now on the client side, I have created the following websocket connection to localhost 3000.

When first rendering the below client code, the data displays where I also get all the console log messages, i.e. ws opened, getting data.... and finally console logging the actual data.

isPaused is also set to false.

The problem I'm facing and unsure what the issue is, is that I expected to see my client page update the country/temp data (no page refresh), when I updated the country/temp values in my_temp_table database table, but it didn't.

The result that I expected was that via the websocket, anytime my table on the server-side updated, the client would update the tempData, via the second useEffect hook below.

I basically would like the client to pull in and display changes from the server via websocket when the data changes in the backend db table.

import React, { useState, useEffect, useRef } from 'react';

export default function Temperature() {

  const [isPaused, setPause] = useState(false);
  const [tempData, setTempData] = useState([]);
  const [name, setName] = useState(null);
  const ws = useRef(null);

  useEffect(() => {
    ws.current = new WebSocket("ws://localhost:3000");
    ws.current.onopen = () => {
      console.log("ws opened");
    }
    ws.current.onclose = () => console.log("ws closed");    

    return () => {
        ws.current.close();
    };
  }, []);

  useEffect(() => {
      if (!ws.current) return;

      ws.current.onmessage = e => {
          if (isPaused) return;
          console.log("getting temp data....");
          const data = JSON.parse(e.data);
          setTempData(data)          
          console.log("data: ",data);
      };
  }, [isPaused]);

  return (
    <div>
        <button onClick={() => setPause(!isPaused)}>
            {isPaused ? "Resume" : "Pause"}
        </button>

        { tempData? 
          tempData.map((data, i) => ( 
            <div>
              <span>{data.country}</span> 
              <span>{data.temp}</span> 
            </div>
          ))
          : null }
    </div>
  )
}
Coeliac answered 7/10, 2021 at 5:55 Comment(0)
N
2

The code is executing only once because there are no recurrying calls to the web socket send event. When the web socket is created it gets the data from the database and sends it, and thats it.

You probably want some kind of action that triggers this event multiple times. For example, in your code:

wss.on("connection", async webSocketClient => {
    console.log("A new client Connected!");

    setInterval(() => {
        const timeNow = Date.now();

        webSocketClient.send(
            JSON.stringify([
                { country: "country-a", temp: timeNow },
                { country: "country-b", temp: timeNow },
            ])
        );
    }, 1000);

    webSocketClient.on("message", message => {
        console.log("received: %s", message);
    });
});

I see you are using some package to pool from a PostgreSQL db. Take a look at this other example.

Nosy answered 7/10, 2021 at 22:33 Comment(0)
S
0

How would your clients know if there is any change in database on server side ? You can create an event that triggers each time a particular data changes and listen to those event on your client sockets. Like you did with onmessage event in your current code.

You can render the react component based on this event.

Staffard answered 7/10, 2021 at 6:2 Comment(8)
I'm a bit unsure on what you mean as websockets are new to me. Could you please elaborate when you say "You can create an event that triggers each time a particular data changes and listen to those event on your client sockets" ? Any example code would really help, just so I understand what you mean.Coeliac
Sorry but this could be writing a full project. But what i suggest if you could get some help from following article. sitepoint.com/real-time-apps-websockets-server-sent-eventsStaffard
So basically SSEs are different to websockets where I would be using something completely different to websockets? In my scenario, the client doesn't need to send data to the server, it only needs to receive data from the server when data changes and reflects these changes in the client with no page refresh. Will server-sent-events perform this requirement?Coeliac
yes in that case at any point of time when server will send an event to all connected clients.(This could be a message being sent to all client or you could just broadcast the message). Based on that message you can set some state in your client app and render the specific component based on state change. You can either send some data to socket or just a simple message and based on that clients can fetch the data from api.Staffard
It would be really useful if there was a simple example that used SSE to match my requirement above. Just to assist with my understanding.Coeliac
I have created a demo project for you please check my repo github.com/brijeshdave/wesocket_server_drivern_eventStaffard
is this the one you needed ?Staffard
still looking into it but will return to it soon - thanks.Coeliac

© 2022 - 2024 — McMap. All rights reserved.