Updating data realtime in Node.js express website - javascript

I'm trying to achieve something what I think should be very simple to do, but all the tutorials and examples I find seem to be an overkill.
What I am doing:
I'm fetching weather info periodically, and I want to update the text on the website everytime its fetched without user having to refresh the browser.
Almost every tutorial on realtime data updating recommends using socket.io and I have given it a try, but I can't get it to do what I want and I'm starting to think that there should be an easier way and that socket.io might not be the best way to go about this. Any suggestions? How do I get simple line of text update on website without user having to refresh the page?
My weather script:
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
function refreshWeather() {
var temperature = getTemperature();
io.sockets.emit('broadcast', {
temperature : temperature
});
}
My jade script:
doctype html
html
head
link(rel='stylesheet', href='/css/index.css')
title Dashboard
body
script(src='/socket.io/socket.io.js')
script.
var socket = io();
socket.on('broadcast', function (data) {
// UPDATE WEATHER HERE
});
.main-content
h1 Weather is: // I WANT THIS TO BE UPDATED

You could:
Get rid of socket.io.
Make an end point for retrieving the temperature.
Do some polling on the frontend.
Backend would look something like this:
var express = require('express');
var app = express();
var server = require('http').Server(app);
app.get('/weather', function (req, res) {
res.send(getTemperature())
});
Frontend would look something like this:
doctype html
html
head
link(rel='stylesheet', href='/css/index.css')
title Dashboard
body
.main-content
h1(id='weather') Weather is: // I WANT THIS TO BE UPDATED
script.
setInterval(function () {
fetch('/some/url', {
method: 'get'
}).then(function(response) {
// UPDATE WEATHER HERE
document.getElementById('weather').innerHTML = response
}).catch(function(err) {
// Error :(
});
}, 1000) // milliseconds
Code is totally untested, so please try not to copy and paste — adapt it instead. Also, if you're going to use the fetch API, you might want to use a polyfill, otherwise, just use plain ajax.

Related

I want to show live data in my node.js app WITHOUT a loop. How do I use mongoDB like Firebase? Is sockets suitable?

A new message gets inserted into the mongoDB from another application that I don't have access to the code. I do have the database credentials though. I want to make that message show on my application at the moment it gets inserted into mongoDB. I don't want to loop. I want to show the new data the moment it gets Inserted or Updated.
A living example: Auction websites generally update the newest bids really quickly, a few seconds before the auction deadline ends. I am wondering how that works without loops. Like a chat app, but waiting for new data in the database instead of another user typing a message. But in my situation, the data gets Inserted or Updated by another application.
Some additional information: I am also using routers / express and hbs html template. I am happy enough if I get atleast a general idea of how to do this, without working with routers / express yet. But if you guys can come up with a combined solution that would be perfect!
I have something like this so far but it's still a bit confusing. You can pretty much ignore my code since it's not working completely. But maybe it helps a bit.
(some more code above ...)
const WebSocketServer = new WebSocket.Server({port: 8082})
WebSocketServer.on('connection', ws => {
console.log('New client connected!')
ws.on('message', async (data) => {
console.log(`Client has sent us: ${data}`)
const connMtlinki = require('../src/db/mtlinki')
const connection = connMtlinki.connect()
const database = connMtlinki.db('MTLINKi')
const MacroVariableHistory = database.collection('MacroVariableHistory')
const machines = await MacroVariableHistory.findOne({ L0Name: data })
console.log(machines)
ws.send(machines)
})
ws.on('close', () => {
console.log('Client has disconnected!')
})

setInterval() in EJS template does not work

I'm currently stuck with a problem in a homework project. I'm trying to make a project where the price of bitcoin will update every second. Now the API request is working fine and I can see the data render from an EJS template but I can't make the price update every second. Can anyone check my code and see if anything is wrong in my code? For reference please check www.preev.com. It shows how I want the price to be updated. And also check below my code.
I have tried to call the API request in app.js file and rendered it in an EJS template called results.ejs.
app.js
var express = require("express");
var app = express();
var request = require("request");
app.set("view engine", "ejs");
app.get("/", function(req, res) {
request("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true", function(error, response, body) {
if(!error && response.statusCode == 200) {
var data = JSON.parse(body);
res.render("result", {data: data});
}
});
});
app.listen(3000, function(){
console.log("server has started");
});
results.ejs
<h1>
Bitcoin Latest
</h1>
Price: $ <span id="showPrice"></span>
<br>
MarketCap: $<%= data["bitcoin"]["usd_market_cap"] %>
<br>
24h Volume: $<%= data["bitcoin"]["usd_24h_vol"] %>
<br>
24h Change: <%= data["bitcoin"]["usd_24h_change"] %>%
<script>
function updatePrice(){
document.getElementById("showPrice").innerHTML= <%= data["bitcoin"]["usd"] %>;
};
setInterval(updatePrice, 500);
</script>
Initial answer
Your setInterval works fine, it's just that inside your function the data never changes.
To fix it you have to reference a variable (of which the content changes), rather than hardcoding the value in your function.
Extra explanation
For example you are using EJS, which is a templating language. A templating language parses output based on your variables (once per page load).
Your template line
document.getElementById("showPrice").innerHTML= <%= data["bitcoin"]["usd"] %>;
parses into
document.getElementById("showPrice").innerHTML= 9624.46;
And your interval then updates the innerHTML of #showPrice with that value, every 500 ms.
What you probably mean to do is make the request from the client (the browser), then store its response into a variable, say latestResult, and then code your function to reference that variable, like so:
document.getElementById("showPrice").innerHTML= latestResult;
Example implementation
This means that your express application (app.js) will render result without data:
app.get('/', function(req, res) {
res.render('result');
});
And the request part will be in your template:
function updateLatestPrice() {
fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true').then((result) => {
const latestResult = result.bitcoin.usd
document.getElementById("showPrice").innerHTML = latestResult || 'failed'
})
}
setInterval(updateLatestPrice, 3000)
Note that I changed request into fetch here because I couldn't be sure whether your client code has babel, so I went with the browser's native Fetch API.

Get variable from client-side JavaScript file in Express

I am trying to send a variable from my client-side JavaScript file to my server-side app.js file. I know that you can get a value from something like a form input field using methods such as the one below (using Express):
var express = require('express');
var app = express();
var path = require('path');
let cityResponse;
app.post('/city', function(req,res) {
cityResponse = {
user_name: req.body.userName
}
res.sendFile(path.join(__dirname, '/client/city/index.html'));
});
But I would like to get a value not directly from the HTML, but from the JavaScript file that the HTML is attached to.
As well as this, I am currently using Socket.io to send the data from the server to the client, and vice versa, using a window.onload to let the server know when the page is ready:
index.js
window.onload = function() {
socket.emit('cityPageReady');
console.log("city page ready");
};
socket.on('cityPageInfo', function(cityResponse) {
console.log('city page info received');
console.log(cityResponse);
console.log(cityResponse.user_name);
userName = cityResponse.user_name;
document.getElementById("name").innerHTML = userName;
});
app.js
var city = io.of('/city').on('connection', (socket) => {
console.log('A user has connected!');
socket.on('cityPageReady', function() {
socket.emit('cityPageInfo', cityResponse);
console.log('city page ready recieved');
});
});
This works, but many people have said that this is overkill, or as one person put it, "using a hammer to kill a bee". Ideally, I'd like to use the optimal method. I do know that template engines can achieve this, but I do not want to have to rewrite all my HTML just to be able to send a single variable to the server.
To reiterate, my questions are:
How would I get a variable from the client-side JavaScript file (not the HTML)?
What is the best way to send these variables back over to client-side?
Any help would be greatly appreciated.

Socket.io: io is not defined

So, I am trying to get data on my node.js file instead of directly getting it on my JS file.
I a using Socket.io 2 here, Below is a snapshot of my code and this is also the first time I am using Websocket with Node.
I am doing something like
var socket = require('socket.io')
//Home Page
app.get("/", function(req, res) {
var cryto = io.connect("https://xyfz.com/);
cryto.on('trades', function (tradeMsg) {
console.log(tradeMsg);
});
res.render("index");
});
I also tried something like
var io = socket("https://abc.io/");
and
var socket = require('socket.io')
var io = socket();
which was throwing errors like server.listeners is not a function and io.connect is not a function.
I am certain that I messing up stuff here so can someone guide me about the correct way of doing it?
Two things which are wrong .
First Consider using Socket.io-client instead of Socket.io.
Second and very much important.
Never ever make API calls inside your routes. This will trigger a API call everytime user opens your website or webpage
You can also expect an error "Unexpected headers" or something like that.
Always, Try do declare it outside any routes in your NodeAPP.
Same goes for socket.io as well

Display data from REST API and update page - Express.JS

I'm using Express.JS and trying to display statistics of my work's HelpDesk system.
The HelpDesk software has it's own REST API. example:("servicedesk.co.uk:8080/sdpapi/request?INPUT_DATA=bla")
Currently, I am calling the API and returning the data I need. In my 'Index.js' I have:
(I'm using node-rest-client)
var express = require('express');
var router = express.Router();
var Client = require('node-rest-client').Client;
var callCount = 0;
/* GET home page. */
router.get('/', function (req, res, next) {
var client = new Client();
client.get("http://servicedesk.co.uk:8080/sdpapi/request?INPUT_DATA=bla", function (data, response) {
// parsed response body as js object
callCount = (data.operation.details.length);
res.render('index', { title: 'ServiceDesk Dashboard', appSupportCallCount: callCount });
});
});
This passes the data I want, to my 'index.hbs'.
What I want, is to somehow monitor the existing REST API, for any changes(i.e. when a new support ticket is raised in the helpdesk system). Then update the data being displayed on my 'index.hbs' - preferrably without the page actually reloading.
I've been looking into socket.io, long polling, etc. but cannot seem to find a way of accomplishing this.
If it cannot be completely real-time. Then I guess I can periodically, make the request to the API and then send the new response to my index.hbs - not sure how I'd do this though.
Thanks in advance for any help provided.

Categories