send multiple data from backend to front end - node.js

I am using the fetch api to get data from backend. The data I am getting is dynamic and more and more data keeps producing. What I want is to send the data to the front end when I get the data in the backend. How can I achieve this? I have coded a sample example of my senario below. Thanks in advance
fetch('/api/blah', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
request: `{"requestType": "numbers"}`
})
})
.then((res) => res.json())
.then(data => {
if (data.status == 'success') {
const numbers = data.numbers
console.log(numbers)
}
});
const distribution = async(req, res) => {
const request = JSON.parse(req.body.request)
if (request.requestType == 'numbers') {
var ceiling = 100;
var floor = 1;
var x = 1;
var step = 1;
setInterval(function() {
res.send({
status: 'success',
numbers: x
})
x += step;
if (x === ceiling || x === floor) {
step = -step;
}
}, 500);
}
}

You can use sockets to get your desired output. You can follow this link for more info Send message to specific client with socket.io and node.js
const
{Server} = require("socket.io"),
server = new Server(8000);
var ceiling = 100;
var floor = 1;
var x = 1;
var step = 1;
let
sequenceNumberByClient = new Map();
// event fired every time a new client connects:
server.on("connection", (socket) => {
console.info(`Client connected [id=${socket.id}]`);
// initialize this client's sequence number
sequenceNumberByClient.set(socket, 1);
// when socket disconnects, remove it from the list:
socket.on("disconnect", () => {
sequenceNumberByClient.delete(socket);
console.info(`Client gone [id=${socket.id}]`);
});
});
//emit your data to specific channel
setInterval(function() {
for (const [client, sequenceNumber] of sequenceNumberByClient.entries()) {
client.emit("numbers", x);
x += step;
if (x === ceiling || x === floor) {
step = -step;
}
}
}, 500);

For passing data from the back-end to the front-end dynamically, you can use jQuery AJAX
https://www.w3schools.com/js/js_ajax_intro.asp
In your case, you can call the AJAX endpoint once every few minutes or so to get the new data.

In REST API structure you can send a response to an API only once. You cannot use setInterval to generate multiple response.
instead you should add all possible data in array and put that array in your response once.
const distribution = async (req, res) => {
const request = JSON.parse(req.body.request);
if (request.requestType == "numbers") {
var ceiling = 100;
var step = 1;
var return_arr = [];
for (let i = 1; i <= ceiling; i += step) {
return_arr.push(i);
}
res.send({
status: "success",
numbers: return_arr,
});
}
};

If I understand correctly you want to send server events from your server to your frontend as soon as data changes on the server. In order to achieve this you will need websocket connections from frontend to server. websockets are a bidirectional connection between your server and client. while the clients are connected to the server via sockets, the server can send events to the frontend without receiving a request first. I suggest using socket.io for this.
You can find more info about socket.io here:
https://socket.io/docs/v4/
There are more alternatives like the websocketAPI. more info about websocketAPI you can find in mozilla webdocs here:
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
Also I found this article on how to implement websockets without socket.io as socket.io is more like a framework on top of normal websockets:
https://www.piesocket.com/blog/nodejs-websocket

Related

WA business api nodejs

I have a problem with my nodejs code and the connection to the official whatsapp business api.
The bot connects the webhook correctly, the messages arrive to the server correctly but the code I have implemented to make it respond is not being effective, I checked the code from top to bottom but I can't find the fault.
I leave you the codes so you have more context:
whatsappController.js:
const fs = require("fs");
const myConsole = new console.Console(fs.createWriteStream("./logs.txt"));
const whatsappService = require("../services/whatsappService")
const VerifyToken = (req, res) => {
try {
var accessToken = "456E7GR****************************";
var token = req.query["hub.verify_token"];
var challenge = req.query["hub.challenge"];
if(challenge != null && token != null && token == accessToken){
res.send(challenge);
}
else{
res.status(400).send();
}
} catch(e) {
res.status(400).send();
}
}
const ReceivedMessage = (req, res) => {
try {
var entry = (req.body["entry"])[0];
var changes = (entry["changes"])[0];
var value = changes["value"];
var messageObject = value["messages"];
if(typeof messageObject != "undefined"){
var messages = messageObject[0];
var text = GetTextUser(messages);
var number = messages["from"];
myConsole.log("Message: " + text + " from: " + number);
whatsappService.SendMessageWhatsApp("The user say: " + text, number);
myConsole.log(messages);
myConsole.log(messageObject);
}
res.send("EVENT_RECEIVED");
}catch(e) {
myConsole.log(e);
res.send("EVENT_RECEIVED");
}
}
function GetTextUser(messages){
var text = "";
var typeMessage = messages["type"];
if(typeMessage == "text"){
text = (messages["text"])["body"];
}
else if(typeMessage == "interactive"){
var interactiveObject = messages["interactive"];
var typeInteractive = interactiveObject["type"];
if(typeInteractive == "button_reply"){
text = (interactiveObject["button_reply"])["title"];
}
else if(typeInteractive == "list_reply"){
text = (interactiveObject["list_reply"])["title"];
}else{
myConsole.log("sin mensaje");
}
}else{
myConsole.log("sin mensaje");
}
return text;
}
module.exports = {
VerifyToken,
ReceivedMessage
}
The second file is whatsappService which I make the connection with the api using the token and I also send the format of the message I want to send when I receive a hello for example...
const https = require("https");
function SendMessageWhatsApp(textResponse, number){
const data = JSON.stringify({
"messaging_product": "whatsapp",
"recipient_type": "individual",
"to": number,
"type": "text",
"text": {
"preview_url": false,
"body": textResponse
}
});
const options = {
host:"graph.facebook.com",
path:"/v15.0/1119744*************/messages",
method:"POST",
body:data,
headers: {
"Content-Type":"application/json",
Authorization:"Bearer EAAWNbICfuWEBAK5ObPbD******************************************************"
}
};
const req = https.request(options, res => {
res.on("data", d=> {
process.stdout.write(d);
});
});
req.on("error", error => {
console.error(error);
});
req.write(data);
req.end();
}
module.exports = {
SendMessageWhatsApp
};
Then I declare the routes for the get (to check token) and post (to receive and reply to messages) methods:
const expres = require("express");
const router = expres.Router();
const whatsappController = require("../controllers/whatsappControllers");
router
.get("/", whatsappController.VerifyToken)
.post("/", whatsappController.ReceivedMessage)
module.exports = router;
Last but not least the index file for the code to run correctly:
const express = require("express");
const apiRoute = require("./routes/routes");
const app = express();
const PORT = process.env.PORT || 3000
app.use(express.json());
app.use("/whatsapp", apiRoute);
app.listen(PORT, () => (console.log("El puerto es: " + PORT)));
I should clarify that I did the tests with Postman and they were all successful, it responds and receives messages correctly, finally I did the tests by uploading the bot to the Azure service and it works without problem until it has to answer/replicate the user's message.
The bot is not responding to the user when he talks to it but everything arrives correctly to the server and it processes it with a 200 response. I attach the evidence that there is no problem in the reception.
Finally I must say that in the meta platform I have everything configured as specified by the same platform, I have already configured the api to answer the messages through the webhooks and everything is correct, I just can't get the bot to answer correctly.
The bot is hosted in the Azure service.
Solved: some numbers have a problema with the api of WAB in my country (Argentina) the phone numbers start in +54 9 11. The problem is the 9 in the phone number, and this have a conflict in the servers of meta, Solution quit number 9 to numbers of this country and the message will send to user.

Using nodejs to make https request to multiple servers

I am trying to make a site for crypto data using coin-gecko's API.
They have 2 different end points for what i require and as such require 2 different URLs.
I had no problem using into the globalUrl to get data such as the total Market cap, volume, etc. which i was able to render into my ejs.
My problem is now i cannot use the other URL for this, seeing as I cannot make another get request, what would be the best way to get data from the topCoinsUrl such as say the "id" of bitcoin from the 2nd url please
const https = require('https');
const app = express();
app.get("/", function(req, res) {
const globalUrl = "https://api.coingecko.com/api/v3/global";
const topCoinsUrl = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false&price_change_percentage=1h"
https.get(globalUrl , function(response) {
let data = "";
response.on("data", function(chunk) {
data += chunk
});
response.on("end", function() {
const globalMarket = JSON.parse(data);
const totalCryptocurrencies = globalMarket.data.active_cryptocurrencies
let totalMarketCap = globalMarket.data.total_market_cap.usd
let totalMarketCapUsd = totalMarketCap.toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
});
let totalVolume = globalMarket.data.total_volume.usd
let total24hVolume = totalVolume.toLocaleString('en-US', {
style: 'currency',
currency: 'USD',
});
let markets = globalMarket.data.markets
let bitcoinMarketShare = Math.round(globalMarket.data.market_cap_percentage.btc);
res.render("home", {
totalCryptocurrencies: totalCryptocurrencies,
totalMarketCap: totalMarketCapUsd,
total24hVolume: total24hVolume,
markets: markets,
bitcoinMarketShare: bitcoinMarketShare
});
})
}).on("error", function(error) {
console.error(error)
});
});
// Ideally i would like to add this to get the ID of bitcoin, but I get an error when i try to use the 2 get requests:
https.get(topCoinsUrl, function(response) {
let data = "";
response.on("data", function(chunk) {
data += chunk
});
response.on("end", function() {
const topCoinsUrl = JSON.parse(data);
let bitcoinId = topCoinsUrl[0].symbol
res.render("home", {
bitcoinId: bitcoinId
})
})
// Error handler
}).on("error", function(error) {
console.error(error)
});
});
If you wish to make 2 simultaneous requests, you should use something like Promise.all() . Create two network requests and fire them at the same time using Promise.all & collect their result.
You can use Blurebird as well... http://bluebirdjs.com/docs/api/promise.all.html

Twitter API v1 stream not working with needle in Node JS (getting no data)

In my project I'm using Twitter API user timeline (both v1 and v2) and filtered stream (v2) with needle successfully, but I can't get the v1 version of filtered stream (statuses/filter) working with needle.
I feel clueless, as needle's streaming works fine with API v2 (just like the official example) and I've tested the authentication part (OAuth) of my v1 code and verified that it works well (response code: 200) - yet I'm not getting any data from the stream.
I even tried finding the problem by setting up Fiddler as a proxy of needle, but I saw nothing strange.
I've created an extracted, stand alone version of the v1 streaming module, hoping that someone can spot the issue/bug.
const uuid = require('uuid');
const oauth = require('oauth-sign');
const needle = require('needle');
const keys = {
twitter: {
consumerKey: '<your-consumerKey>', //API Key
consumerSecretKey: '<your-consumerSecretKey>', //API Secret Key
oauthToken: '<your-oauthToken>', //Access Token
oauthTokenSecret: '<your-oauthTokenSecret>', //Access Token Secret
}
};
const streamURL = 'https://stream.twitter.com/1.1/statuses/filter.json';
const apiParams = {
'track': 'pizza',
};
function getAuthHeader() {
const timestamp = Date.now() / 1000;
const nonce = uuid.v4().replace(/-/g, '');
let oauthParams = {
'oauth_consumer_key': keys.twitter.consumerKey,
'oauth_nonce': nonce,
'oauth_signature_method': 'HMAC-SHA1',
'oauth_timestamp': timestamp,
'oauth_token': keys.twitter.oauthToken,
'oauth_version': '1.0'
};
let mergedParams = { ...apiParams, ...oauthParams };
oauthParams['oauth_signature'] = oauth.hmacsign(
'POST', streamURL, mergedParams,
keys.twitter.consumerSecretKey,
keys.twitter.oauthTokenSecret
);
return Object.keys(oauthParams).sort().map(function (k) {
return k + '="' + oauth.rfc3986(oauthParams[k]) + '"';
}).join(', ');
}
function streamConnect(retryAttempt) {
const stream = needle.post(streamURL, apiParams, {
headers: {
'Authorization': `OAuth ${getAuthHeader()}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
timeout: 20000
});
stream.on('header', (statusCode, headers) => {
console.log(
`Status: ${statusCode} ` +
`(${statusCode === 200 ? 'OK' : 'ERROR'})`
);
}).on('data', data => {
//never receiving any data ???
console.log('data received:');
console.log(data);
}).on('err', error => {
// This reconnection logic will attempt to reconnect when a disconnection is detected.
// To avoid rate limits, this logic implements exponential backoff, so the wait time
// will increase if the client cannot reconnect to the stream.
let retryTimeout = 2 ** retryAttempt;
console.warn(`A connection error occurred: ${error.message}. Reconnecting in ${retryTimeout/1000} seconds...`)
setTimeout(() => {
streamConnect(++retryAttempt);
}, retryTimeout);
});
return stream;
}
streamConnect(0);
(I know that there are a couple of libraries that I could use, but 1: I don't want to use one just because of this bug, 2: I truly hate when I can't find a solution for a problem)

RxJS Schedulers

I'm doing some simple experiments with RxJS in a simple NodeJS Express server where I am comparing different approaches to handling and processing requests (based on this post https://snyk.io/blog/nodejs-how-even-quick-async-functions-can-block-the-event-loop-starve-io/). This is the basic setup:
const express = require('express')
const crypto = require('crypto')
const { asyncScheduler, asapScheduler, range } = require('rxjs')
const { promisify } = require('util')
const setImmediatePromise = promisify(setImmediate)
const PID = process.pid
function log(msg) {
console.log(`[${PID}]`, new Date(), msg)
}
const app = express()
function randomString() {
return crypto.randomBytes(100).toString('hex')
}
app.get('/compute-sync', function computeSync(req, res) {
log('computing sync!')
const hash = crypto.createHash('sha256')
for (let i = 0; i < 1e6; i++) {
hash.update(randomString())
}
res.send(hash.digest('hex') + '\n')
})
app.get('/compute-immediate', function computeImmediate(req, res) {
log('computing immediate!')
const hash = crypto.createHash('sha256')
for (let i = 0; i < 1e6; i++) {
await setImmediatePromise(hash.update, randomString())
}
res.send(hash.digest('hex') + '\n')
})
app.get('/compute-rxjs', async function computeRxjs(req, res) {
log('computing Rxjs!')
const hash = crypto.createHash('sha256')
range(0, 1e6, asapScheduler).subscribe({
next() {
hash.update(randomString())
},
complete() {
res.send(hash.digest('hex') + '\n')
},
})
})
app.get('/healthcheck', function healthcheck(req, res) {
log('they check my health')
res.send('all good!\n')
})
const PORT = process.env.PORT || 1337
let server = app.listen(PORT, () => log('server listening on :' + PORT))
It was my understanding that the asapScheduler would use setImmediate under the hood, so why does the /compute-immediate endpoint NOT block the event loop (keeps the server responsive to new requests) but the /compute-rxjs does block and leads to server timeouts on the health endpoint?
I have also tried the asyncScheduler - this does not block, but it does take perhaps an order of magnitude longer to complete then the /compute-immediate endpoint.
I would really like to use RxJS for more complex processing of incoming requests, but feel this issue makes that choice undesirable. Is there something I am missing? Is there a way to get the RxJS solution to work in the same way as the setImmediate solution?
Thanks for your responses!
I now have a solution that I based on this gist: https://gist.github.com/neilk/5380684
The /compute-rxjs now looks like this:
app.get('/compute-rxjs', function computeRxjs(req, res) {
log('computing Rxjs!')
const hash = crypto.createHash('sha256')
new Observable(subscriber => {
;(iter = (i = 0, max = 1e6) => {
if (i === max) return subscriber.complete()
subscriber.next(i)
return setImmediate(iter, i + 1, max)
})()
}).subscribe({
next() {
hash.update(randomString())
},
complete() {
res.send(hash.digest('hex') + '\n')
},
})
})
It seems to behave exactly as I wanted (recursion - who knew?) - it does not block the event loop, and takes the same amount of time to run as the /compute-immediate endpoint, but gives me the flexibility to use the RxJS pipeline capabilities.

Nodejs /Net-SNMP :Error send snmp-walk response to client

I want create project for monitoring server by snmp. i use net-snmp component and express.i have error "Cannot set headers after they are sent to the client" when i want send response snmp walk to client.
I write the code.
var snmp = require('net-snmp');
var express = require('express');
app.on('/api/snmp/', (req, res) => {
const session = new snmp.createSession(req.query.IP, req.query.Community);
session.walk(
'1.3.6.1',
20,
function(varbinds) {
let msg = [];
for (var i = 0; i < varbinds.length; i++) {
if (snmp.isVarbindError(varbinds[i])) {
console.log('error1');
} else {
console.log('send');
msg.push({ message: varbinds[i].oid + '|' + varbinds[i].value });
}
}
console.log(res.getHeaders());
// res.setHeader('Content-Type', 'application/json');
// res.send(msg);
res.end(msg);
// resolve(resultStr);
},
function(error) {
console.log('error2');
console.log(error.toString()); // show error in console: Cannot set headers after they are sent to the client
}
);
});
I can find solution when I convert type of "msg" variable to string that is worked.
but i dont know why this have problem object type.

Resources