Handle Multiple HTTP requests in Node JS - node.js

Problem Statement:
Consider a node js program running with express library for REST communication.
There are multiple REST API calls and all the API's are using a single blocking resource which can run only one request at a time like serial port.
I have a situation that I receive multiple parallel requests to the API calls which inturn use the serial port for some purpose.
How do I channelize the API calls from different methods and then streamline it and then execute it one by one and then return back the response to the corresponding request ?
Code Snippet:
/**
* This function is a blocking function that utilizes the serial port
*/
function request_response(shield, channel, parameter, parameterValue) {
var promise = new Promise(function (resolve, reject) {
var commandString = modbusRTURequestGenerator.modbusRTURequestGenerator(shield, channel, parameter, data, parameterValue);
serialOUT(commandString).then(() => {
Promise.race([serialIN(), timeOut()]).then(results => {
resolve(results);
});
});
});
return promise;
}
/**
* This is a GET Request API to get the parameter values.
*/
router.get("/getParameter", function (req, res) {
var shield = req.query.shield;
var channel = req.query.channel;
var parameter = req.query.parameter;
request_response(shield, channel, parameter, undefined).then(
(result) => {
res.send(result);
});
});
/**
* This is a POST Request API to set the parameter values.
*/
router.post("/setParameter", function (req, res) {
var shield = req.query.shield;
var channel = req.query.channel;
var parameter = req.query.parameter;
var parameterValue = req.body.value;
request_response(shield, channel, parameter, parameterValue).then(
(result) => {
res.send(result);
});
});
/**
* This is a GET Request API for Scan functionality.
*/
router.get("/scan", function (req, res) {
var msg = {};
var cmds_arr = [];
for (var i = 1; i <= constants.NUMBER_OF_SHIELDS; i++) {
cmds_arr.push(i + "," + constants.SCAN_COMMAND_REGISTER);
}
asyncLoop(cmds_arr, function (item, next) {
var params = item.split(",");
var shield = params[0];
var register = params[1];
request_response(shield, undefined, register, undefined).then(
(result) => {
msg[data] = result;
next();
}
});
}, function () {
res.send(msg);
});
});
There are 3 kinds of HTTP REST API's: scan, getParameter and setParameter. All these 3 are accessing the request_response function. So, when I run the scan API and then trigger the getParameter API parallely, I am getting false results.

Related

page renders before getting all the values sorted

I think the rendering takes place before the searching of the string on the files, i have tried different methods but don't seems to get this working. any help will be appreciated. im a noob on to the nodejs. im trying to get the id of the user and query and get all the data and there after see if he is in any of the lists given and finally render the page.
const j = [];
let name = '';
const filename = [];
var ext = '';
module.exports = function(app, express) {
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/cusdetails', isLoggedIn, function (req, res) {
var cusid=req.body.cusid;
var insertQuerys = "SELECT * FROM customer WHERE cusid=? ORDER BY rowid DESC LIMIT 1";
connection.query(insertQuerys,[cusid],
function(err, rows){
rows.forEach( (row) => {
name=row.fncus;
});
fs.readdir('./views/iplist', function(err, files) {
if (err)
throw err;
for (var index in files) {
j.push(files[index])
}
j.forEach(function(value) {
var k = require('path').resolve(__dirname, '../views/iplist/',value);
fs.exists(k, function(fileok){
if(fileok) {
fs.readFile(k, function(err, content) {
if (err) throw err;
if (content.indexOf(name) > -1) {
ext = path.extname(k);
filename.push(path.basename(k, ext));
}
});
}
else {
console.log(" FileNotExist ");
}
});
});
});
console.log(filename);
res.render('cusdetails.ejs', {rows: rows, user:req.user , aml: filename });
});
})
You can create simple Promise wrapper and then use it inside async/await function to pause execution until resolved.
// use mysql2 package as it provides promise, less work to write promise wrappers
const mysql = require('mysql2/promise');
// create the connection to database
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
database: 'test'
});
// sample wrapper
function some(k) {
// more advisable to have local variables, why do you need this to be array?
var filename = [];
return new Promise((resolve, reject) => {
// doing this is also not recommended check nodejs documentation **fs.exists** for more info
fs.exists(k, function(fileok){
if(fileok) {
fs.readFile(k, function(err, content) {
if (err) reject(err);
if (content.indexOf(name) > -1) {
ext = path.extname(k);
filename.push(path.basename(k, ext));
resolve(filename)
}
});
}
else {
// reject(new Error("FileNotExist"))
console.log(" FileNotExist ");
}
});
})
}
// note the use of async
app.post('/cusdetails', isLoggedIn, async function (req, res) {
var cusid=req.body.cusid;
var insertQuerys = "SELECT * FROM customer WHERE cusid=? ORDER BY rowid DESC LIMIT 1";
// using await to pause excution, waits till query is finished
const [rows] = await connection.query(insertQuerys,[cusid])
rows.forEach( (row) => {
name=row.fncus;
});
// then you can
var result = await some(k)
...
Note however this way you loose the advantage of concurrent execution, as it's kindoff blocking. If the result of one call is not used in another, you can execute in parallel and await for result to achieve sequencing like
const [rows] = connection.query(insertQuerys,[cusid])
var result = some(k)
console.log(await rows) // do something
console.log(await result) // do something
JavaScript is asynchronous. This means that if you have a function with a callback (i.e. your query), the callback will be called asynchronously, at an unknown time, while the other code executes.
You need to look up some tutorials how to deal with callbacks, to get a proper understanding of it. Another method is using async/await and/or promises.
Basically, if you take the following code:
console.log("this will print first");
setTimeout(function () {
console.log("this will print last");
}, 1000);
console.log("this will print second");
If you run the code above, the top level is executed synchronously, so, it first calls console.log, then it executes setTimeout, which is synchronous. It sets a timeout, then says "I'm ready", and the code continues to the other console.log. After 1 second (1000 milliseconds), the callback in the setTimeout function is executed, and only then that console.log is called. You can not make the rest of the code wait this way, you need to restructure your code or read into promises.

Not able to get callback to work

I am building an API using ExpressJS, NodeJS
The issue is when I call my API using Postman, I do not get any returned results. I do not know how to make Postman wait for the function to return the allproduct result. I am using callback but it is just not working, I have tried many simple callback codes in my Services part of the code but none of them work. Only Async Await makes the Postman API stop and wait for result, however i am using a third party API called Pipedrive, which works only with callback. If i can somehow make the Pipedrive API work with Async/Await it might solve my issue
Route:
var express = require('express')
var router = express.Router()
// Push the job to different controller functions
var PipedriveController = require('../../controllers/pipedrive.controller');
router.get('/products', PipedriveController.pipedriveAllProducts)
// Export the router
module.exports = router;
Controller
var PipedriveService = require('../services/pipedrive.service')
// Async Controller Function
exports.pipedriveAllProducts = async function(req, res, next){
// let family = req.param.options;
try {
let all_products = await PipedriveService.pipedriveAllProducts()
// Return All product liist with Appropriate HTTP header response
return res.status(200).json({status: 200, all_products});
} catch(e){
// Return an Error Response Message
return res.status(400).json({status: 400, message: e.message});
}
}
Service:
var Pipedrive = require('pipedrive');
var pipedrive = new Pipedrive.Client('SECRET', { strictMode: true });
// Saving the context of this module inside the _the variable
_this = this
exports.pipedriveAllProducts = async function operation(options){
// Array of product object - after which will be converted to JSON
const allproducts = [];
function iterateprods (err, products) {
if (err) throw err;
for (var i = 0; i < products.length; i++) {
// console.log(products[i].prices["0"].price);
let product = {
"id": products[i].code,
"name": products[i].name,
"price": products[i].prices["0"].price
}
allproducts.push(product)
}
console.log(JSON.stringify(allproducts));
return allproducts
}
pipedrive.Products.getAll({},iterateprods)
}
First, no need tu put async before the operation function, you need to wrap your service in a promise, you can do something like this :
var Pipedrive = require('pipedrive');
var pipedrive = new Pipedrive.Client('SECRET', { strictMode: true });
// Saving the context of this module inside the _the variable
_this = this
exports.pipedriveAllProducts = function operation(options){
// Array of product object - after which will be converted to JSON
const allproducts = [];
return new Promise((resolve, reject) => {
pipedrive.Products.getAll({}, function(err, products){
if (err) reject(err);
for (var i = 0; i < products.length; i++) {
// console.log(products[i].prices["0"].price);
let product = {
"id": products[i].code,
"name": products[i].name,
"price": products[i].prices["0"].price
}
allproducts.push(product)
}
console.log(JSON.stringify(allproducts));
resolve(allproducts);
});
}

How could I improve my nodeJS program

I used promises, callbacks and external API for the firt time but I'm not sure that's the best way to use them.
My program traslates words from a langage to another using a langae Pivot and systran.io API.
the function Translate will translate word and send response via a callback.
then in the POST request I used promises to chain tasks.
var express = require('express');
var request = require('request');
var router = express.Router();
router.post("/", function(req, res) {
console.log
var resultat
var promise = new Promise((resolve, reject) => {
translate(req.query.source, "en", req.query.content, function(resa) {
resolve(resa);
})
}).then(function(resolve) {
console.log(resolve);
translate("en", req.query.target, resolve, function(resa2) {
console.log(resa2);
})
});
});
function translate(source, target, content, callback) {
let result;
result = request("https://api-platform.systran.net/translation/text/translate?input=" + content + "&source=" + source + "&target=" + target + "&key=xxxxxxxx-783f-4f90-aea4-7fb357016647", function(err, data, body) {
body = JSON.parse(body);
console.log(body);
callback(body.outputs[0].output)
})
}
module.exports = router;
Is there a best way to write my program which is already working ?

How do I preload value on variable NodeJS

I am just learning NodeJS and I come from a Java/Scala background.
I am writing a service that communicates with Amazon SNS and handles endpoints/tokens.
Basically there is a list of SNS applications that I have in my environment, and this list is rarely modified, so I would like to pre-load its values into a variable or constant on server startup.
The SNS-SDK provided by Amazon has this function for listing the applications:
listPlatformApplications(params, callback)
So what I naively tried to do was this:
var applications = [];
var loadApplications = function() {
sns.listPlatformApplications({}, function(err, data){
if (err) {
console.log(err);
} else {
return data['PlatformApplications'].map(function (app) {
return app['PlatformApplicationArn']
});
}
});
}
loadApplications();
And basically what happens is that some calls come in before this callback finishes, when the list is still empty.
How would I go about pre-loading this data, or any other data, before the server starts responding to requests?
Or maybe my reasoning for this is wrong, and there would be another approach to handle this on NodeJS that is more idiomatic
If you absolutely must use a callback instead of a promise (spoiler alert: you don't), you have to call the server startup command within the SNS callback. So you would do something like this:
var startServer = require('my-server.js');
var applications = [];
var loadApplications = function() {
sns.listPlatformApplications({}, function(err, data){
if (err) {
console.log(err);
} else {
var snsData = data['PlatformApplications'].map(function (app) {
return app['PlatformApplicationArn']
});
startServer(snsData)
}
});
}
loadApplications();
== RECOMMENDED SOLUTION ==
If instead, you can use promises (which you absolutely should!), you could start the SNS request at server start and await the result whenever needed. Your code would look something like this:
const startServer = require('my-server.js');
const loadApplications = async () => {
const data = sns.listPlatformApplications({}).promise();
const snsData = data['PlatformApplications'].map(function (app) {
return app['PlatformApplicationArn']
});
return snsData;
};
const applications = loadApplications();
startServer(applications);
// inside my-server.js
const startServer = async (applications) => {
const doSomethingWithApplications = await applications;
...
}

node.js server handle request callback function ending before writing response

I have an http server with a handleRequest callback that runs another script in vm.runInNewContext for each request. The script that runs inside vm.runInNewContext makes some asynchronous http post requests and writes the server response only after getting the responses from the posts.
As a result, the code of handleRequest callback ends before the server response is written.
Is it safe? or is there a way to avoid this situation?
Here is some code:
var server = http.createServer(handleRequest);
server.listen(8080);
var handleRequest = function (request, response) {
// get request data...
var context = {
ServerRequest : request,
ServerResponse : response
};
var stringScript = // a string with the script that posts data
var script = vm.createScript(stringScript);
script.runInNewContext({ context: context });
}
the script string does this:
var request = require('request');
var options = {....}
var req = request.get(options);
req.on('response', function (res) {
var chunks = [];
res.on('data', function(chunk) {
chunks.push(chunk);
});
res.on('end', function() {
var buffer = Buffer.concat(chunks);
var encoding = res.headers['content-encoding'];
if (encoding == 'gzip') {
zlib.gunzip(buffer, function(err, decoded) {
// set response headers and write the response
context.ServerResponse.end(decoded.toString());
});
} else if (encoding == 'deflate') {
zlib.inflate(buffer, function(err, decoded) {
// set response headers and write the response
context.ServerResponse.end(decoded.toString());
})
} else {
// set response headers and write the response
context.ServerResponse.end(buffer.toString());
}
});
});
Simple solution: Return a promise (e.g. use the Q-library) from the VM-script.
script.runInNewContext will return whatever you return from the VM-script. That way you have a "callback" for when the VM code finishes.
// Script for VM
// I simplified it. Just resolve or reject the promise whenever you are done with your work
'use strict';
var defer = q.defer();
doABarrelRoll(function() {
defer.resolve('RESULT');
});
defer.promise; // This line will return the promise.
When returning a value from a VM-script, you do not need any return construction. Just write the thing you want and let the magic happen.
// Script for current context
'use strict';
var server = http.createServer(handleRequest);
server.listen(8080);
var handleRequest = function (request, response) {
// get request data...
var context = {
ServerRequest : request,
ServerResponse : response
};
var stringScript = // a string with the script that posts data
var script = vm.createScript(stringScript);
var prom = script.runInNewContext({
context: context,
q: require('q'),
});
prom.done(function ($result) {
console.log('VM finished with result: ' + $result);
});
}

Resources