I am trying to invoke services in node express in a loop. But the problem is that before all the services are invoked, the remaining code is getting executed.
I tried some options with Promise, async/await but they didn't work.
Basically I need to invoke the service in a synchronous way.
I have created 2 mock services in JSON stub. In the first service response, I will get an array of vehicles. Once I got this, I need to update one more value in each array by calling another service.
Here the problem I faced is that the 2nd service is not called synchronously.
const express = require('express');
const request = require("request");
const router = express.Router();
router.get('/make', (req, res) => {
var options = {
headers: {
'Content-Type': 'application/json',
'JsonStub-User-Key': 'ddc159a0-5aa8-4a38-a0f1-913e4d768b56',
'JsonStub-Project-Key': '34ba28a9-471c-435d-ab61-b7732c9583c6'
},
method: "GET",
json: true,
strictSSL : false,
url: `http://jsonstub.com/vehicle/make`
};
request(options, function(error, response, body) {
if (body){
checkModelType(body);
res.status(200).json(body).end();
} else {
console.log("REST Request timeout: ", JSON.stringify(error));
res.status(400).json('Error').end();
}
});
});
function checkModelType(response){
let vehicleList = response.vehicleList;
console.log("--->"+vehicleList.length);
for(var i = 0;i<vehicleList.length;++i){
const modelType = findModel();
vehicleList[i].modelType = modelType;
}
console.log("Updated Vehicle List:"+JSON.stringify(vehicleList));
}
const findModel = () =>{
var options = {
headers: {
'Content-Type': 'application/json',
'JsonStub-User-Key': 'ddc159a0-5aa8-4a38-a0f1-913e4d768b56',
'JsonStub-Project-Key': '34ba28a9-471c-435d-ab61-b7732c9583c6'
},
method: "GET",
json: true,
strictSSL : false,
url: `http://jsonstub.com/vehicle/details`
};
request(options, function(error, response, body) {
if (body){
console.log("Model Type-->"+body.output.modelType);
return body.output.modelType;
} else {
console.log("REST Request timeout: ", JSON.stringify(error));
}
});
}
module.exports = router;
Response :
-----------
PS F:\workSpace_Node\TestApp> node app.js
server running at 9086
--->4
Updated Vehicle List:[{"make":"Audi","model":"A3","vin":"QVFCFQT7894563214"},{"make":"Audi","model":"A4","vin":"ASECFQT7894563214"},{"make":"Audi","model":"Q5","vin":"QWECFQT7894993214"}]
Model Type-->SD
Model Type-->SD
Model Type-->SD
Expected result :
[{"make":"Audi","model":"A3","modelType":"SD", "vin":"QVFCFQT7894563214"},{"make":"Audi","model":"A4","modelType":"SD","vin":"ASECFQT7894563214"}]
You can switch to request-promise library instead of request and then go with some async\await:
const express = require('express');
const request = require('request-promise'); // switched library
const router = express.Router();
router.get('/make', async (req, res) => {
var options = {
headers: {
'Content-Type': 'application/json',
'JsonStub-User-Key': 'ddc159a0-5aa8-4a38-a0f1-913e4d768b56',
'JsonStub-Project-Key': '34ba28a9-471c-435d-ab61-b7732c9583c6'
},
method: "GET",
json: true,
strictSSL : false,
url: `http://jsonstub.com/vehicle/make`
};
const body = await request(options);
if (body) {
await checkModelType(body);
res.status(200).json(body).end();
} else {
console.log("REST Request timeout: ", JSON.stringify(error));
res.status(400).json('Error').end();
}
});
async function checkModelType(response){
let vehicleList = response.vehicleList;
console.log("--->"+vehicleList.length);
for(var i = 0;i<vehicleList.length;++i){
const modelType = await findModel();
vehicleList[i].modelType = modelType;
}
console.log("Updated Vehicle List:"+JSON.stringify(vehicleList));
}
const findModel = async () =>{
var options = {
headers: {
'Content-Type': 'application/json',
'JsonStub-User-Key': 'ddc159a0-5aa8-4a38-a0f1-913e4d768b56',
'JsonStub-Project-Key': '34ba28a9-471c-435d-ab61-b7732c9583c6'
},
method: "GET",
json: true,
strictSSL : false,
url: `http://jsonstub.com/vehicle/details`
};
const body = await request(options);
if (body){
console.log("Model Type-->"+body.output.modelType);
return body.output.modelType;
} else {
console.log("REST Request timeout: ", JSON.stringify(error));
}
}
module.exports = router;
And it will change the order of operations:
--->4
Model Type-->SD
Model Type-->SD
Model Type-->SD
Model Type-->SD
Updated Vehicle List:[{"make":"Audi","model":"A3","vin":"QVFCFQT7894563214","modelType":"SD"},{"make":"Audi","model":"A4","vin":"ASECFQT7894563214","modelType":"SD"},{"make":"Audi","model":"Q7","modelType":"SD"},{"make":"Audi","model":"Q5","vin":"QWECFQT7894993214","modelType":"SD"}]
Related
this is my code :
Express Routes:
router.route('/block')
.post(controller.ticketBlocking);
Express Controller:
const axios = require('axios');
const OAuth = require('oauth-1.0a');
const crypto = require('crypto');
const ticketBlocking = (req, res) => {
const data = JSON.stringify({
source = req.body.source
});
const oauth = OAuth({
consumer: {
key: '....', //Hided the key
secret: '....', //Hided the secret
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return crypto.createHmac('sha1', key).update(base_string).digest('base64');
}
});
const request_data = {
url: 'http://link.vvv/blockTicket',
method: 'post',
};
axios({
method: request_data.method,
url: request_data.url,
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...oauth.oauth.toHeader(oauth.oauth.authorize(request_data)),
},
data : data
})
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
} else if (error.request) {
console.log(error.request);
} else {
console.log('Error', error.message);
}
console.log(error.config);
});
};
the npm package which am using is - "oauth-1.0a"
The problem am facing is, when i use GET method with different end point, i get an output but when ever i use POST method am getting an empty error with status code 500
I dont know where is the mistake, am using oauth1.0a for authorization, please help !
The following is my code:
async function asynccall() {
//POST request (create order)
var data = JSON.stringify({
merchant_urls: {
terms: "https://www.example.com/terms.html",
checkout: "https://atelierdecosmetique.herokuapp.com/checkout",
confirmation: "https://atelierdecosmetique.herokuapp.com/confirmation",
push: "https://www.example.com/api/push",
},
});
var config = {
method: "post",
url: "https://api.playground.klarna.com/checkout/v3/orders/",
headers: {
"Content-Type": "application/json",
Authorization: "",
},
data: data,
};
var orderid = Postrequest.data.order_id;
//GET Request (read order)
var axios = require("axios");
var data1 = JSON.stringify({
merchant_urls: {
confirmation: "https://www.example.com/confirmation.html" + Postrequest.data.order_id,
},
});
var config1 = {
method: "get",
url: "https://api.playground.klarna.com/checkout/v3/orders/",
headers: {
"Content-Type": "application/json",
Authorization:
},
data: data1,
};
//The calls as variables
var Postrequest = await axios(config);
var Getrequest = await axios(config1);
console.log(Getrequest.data.merchant_urls.confirmation)
app.get("/checkout", function(req, res) {
res.render("checkout.ejs", {
datapost: Postrequest.data.html_snippet
})
});
app.get("/confirmation", function(req, res) {
res.render("confirmation.ejs", {
dataget: Getrequest.data.html_snippet
});
});
}
asynccall();
My problem with this code is that the Postrequest.data.order_id is not shown in the GET request's merchant_urls.confirmation URL when I console log it at the end of the code. It should return the confirmation page URL with the order_id response from the POST request at the end. How could I solve this? I know it has to do with asynchronous and synchronous code? I'm stuck and really need this to work.
You need to get the results of the first request before using the returned data in the second.
async function asynccall() {
var axios = require("axios");
//POST request (create order)
var data = JSON.stringify({
merchant_urls: {
terms: "https://www.example.com/terms.html",
checkout: "https://atelierdecosmetique.herokuapp.com/checkout",
confirmation: "https://atelierdecosmetique.herokuapp.com/confirmation",
push: "https://www.example.com/api/push",
},
});
var config = {
method: "post",
url: "https://api.playground.klarna.com/checkout/v3/orders/",
headers: {
"Content-Type": "application/json",
Authorization: "",
},
data: data,
};
var Postrequest = await axios(config);
var orderid = Postrequest.data.order_id;
//GET Request (read order)
var data1 = JSON.stringify({
merchant_urls: {
confirmation: "https://www.example.com/confirmation.html" + Postrequest.data.order_id,
},
});
var config1 = {
method: "get",
url: "https://api.playground.klarna.com/checkout/v3/orders/",
headers: {
"Content-Type": "application/json",
Authorization: ""
},
data: data1,
};
//The calls as variables
var Getrequest = await axios(config1);
console.log(Getrequest.data.merchant_urls.confirmation)
app.get("/checkout", function(req, res) {
res.render("checkout.ejs", {
datapost: Postrequest.data.html_snippet
})
});
app.get("/confirmation", function(req, res) {
res.render("confirmation.ejs", {
dataget: Getrequest.data.html_snippet
});
});
}
asynccall();
I have problems with the synchronous and asynchronous functions on NodeJS, here is my problem,
I have 4 functions, a global variable (users), and a rendering function to have the html page.
the scraper1 and scraper2 function can be performed asynchronously, they scrape a website and fill in the global user variable, and the data_selector1 cannot be done unless scraper1 has done the same for data_selector2 with the scraper2 functions.
1-I need that scraper1 and scraper2 work asynchronously and fill the available users, and only render the HTML page if the 2 scrapers have finished their work.
2-I need an animation to be displayed in the browser during the drilling process, how can I do this
Here is what I tried ..
var express = require('express');
var request = require('request');
var cheerio = require('cheerio');
var fs = require('fs');
var router = express.Router();
/* globale variable to populate */
var users = {
'name':null,
'age':null,
};
//function of scraping link1
function scarper1(callback){
console.log("-------------scraper---------");
var url = 'link1';
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function(error, response, body) {
if (error) throw error;
// call the data-selector1 after scraper are finish rendering
data_selector1(body);
}
);
}
//function of scraping link2
function scarper2(callback){
console.log("-------------scraper2---------");
var url = 'link2';
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function(error, response, body) {
if (error) throw error;
// call the data-selector2 after scraper are finish rendering
data_selector2(body);
}
);
}
function data_selector1(body)
{
console.log("-------------data-selector---------");
const $ = cheerio.load(body);
$("div[class='.user']").each(function(i,elem){
users['name'] =$(elem).find('span[class=".name]').text();
users['age'] =$(elem).find('span[class=".age]').text();
});
}
function data_selector2(body)
{
console.log("-------------data-selector2---------");
const $ = cheerio.load(body);
$("ul[class='.user']").each(function(i,elem){
users['name'] =$(elem).find('li[class=".name]').text();
users['age'] =$(elem).find('li[class=".age]').text();
});
}
/* GET home page. */
router.post('/recherche', function(req, res, next) {
// i dont know how to make it here to say that scraper1 and scraper2 can be executed async and to render page after that the two scraper are finished
// and while scraper are working to display animation in the client
scarper1(function(results){
console.log(results);res.render('rechercher', { title: 'Express' });
});
});
You can use promise.all() to do that but before you can use it, you need to promisify your functions:
function scarper1(callback) {
return new Promise((resolve, reject) => {
console.log("-------------scraper---------");
var url = 'link1';
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function (error, response, body) {
if (error) reject(error);
// call the data-selector1 after scraper are finish rendering
data_selector1(body);
resolve('Done successfully');
}
);
});
}
function scarper2(callback) {
return new Promise((resolve, reject) => {
console.log("-------------scraper2---------");
var url = 'link2';
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function (error, response, body) {
if (error) reject(error);
// call the data-selector2 after scraper are finish rendering
data_selector2(body);
resolve('Done successfully');
}
);
});
}
let scarper1 = scarper1(function(results){
console.log(results);res.render('rechercher', { title: 'Express' });
});
let scarper2 = scarper2(function(results){
console.log(results);res.render('rechercher', { title: 'Express' });
});
Promise.all([scarper1, scarper2]).then(function(values) {
console.log(values);
});
For more about promise.all check this docs.
An even better approach is using async.eachLimit() to loop through requests (asynchronously) but first you need to install async package then merge both scraper functions:
const async = require("async");
let urls = [
'link1',
'link2'
]
async.eachLimit(urls, 2, (url) => {
console.log("-------------scraper---------");
request(
{
method: 'GET',
url: 'http://api.myscarperwebservice.com/?url=' + url,
headers: {
Accept: 'application/json',
},
},
function (error, response, body) {
if (error) reject(error);
// call the data-selector1 after scraper are finish rendering
if(url == 'link1')
data_selector1(body);
else
data_selector2(body);
resolve('Done successfully');
}
);
}, (err) => {
console.log("Finished all urls")
});
I have several API Get request at once in nodejs. Each API have new data every couple minutes.
var express = require('express');
var router = express.Router();
var request = require("request");
let value1, value2, bodyData1, bodyData2;
var options = { method: 'GET',
url: 'https://api.example.com/data1',
qs:
{
valueType: 'MAXIMUM'
},
headers:
{
authorization: 'ABC123456',
accept: 'application/json; charset=utf-8' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
bodyData1 = JSON.parse(body);
value1 = bodyData1.value;
});
var options = { method: 'GET',
url: 'https://api.example.com/data2',
qs:
{
valueType: 'MAXIMUM'
},
headers:
{
authorization: 'ABC123456',
accept: 'application/json; charset=utf-8' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
bodyData2 = JSON.parse(body);
value2 = bodyData2.value;
});
router.get('/', function(req, res, next) {
res.render('home', {valueA : value1, valueB: value2});
});
module.exports = router;
I want to know if it is possible to combine them into one function?
Any other things I should concern?
It is possible if you have promises which is currently not the case. You have to wrap your request() call in a Promise. You can do it manually with a custom function requestToPromise.
You can then use Promise.all to call multiple promises in parallel.
function requestToPromise(options) {
return new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) return reject(error);
resolve(body);
});
});
}
var optionsRequest1 = {
method: "GET",
url: "https://api.example.com/data1",
qs: {
valueType: "MAXIMUM"
},
headers: {
authorization: "ABC123456",
accept: "application/json; charset=utf-8"
}
};
var optionsRequest2 = {
method: "GET",
url: "https://api.example.com/data2",
qs: {
valueType: "MAXIMUM"
},
headers: {
authorization: "ABC123456",
accept: "application/json; charset=utf-8"
}
};
var requestPromise1 = requestToPromise(optionsRequest1);
var requestPromise2 = requestToPromise(optionsRequest2);
Promise.all([requestPromise1, requestPromise2]).then(results => {
var [resultPromise1, resultPromise2] = results;
}).catch(error => {
//handle error
});
Instead of using the custom function requestToPromise you can also use util.promisify
const util = require('util');
const requestAsync = util.promisify(request);
Promise.all([requestAsync(optionsRequest1), requestAsync(optionsRequest2)]).then(results => {
var [resultPromise1, resultPromise2] = results;
}).catch(error => {
//handle error
});
You can use Redis cache to store data in memory for fast retrieval and fetch from memory very quickly.
Also, after some interval, you can add them to a database through bulk creation. It will decrease your database call.
// Example in sequilize
await db.table_name.bulkcreate([ {0bj1}, {obj2}..,{obj3 } ]);
I'm learning nodejs and trying to make an API call. The API uses JWT to authenticate.
I created these functions to sign a token:
function token() {
const payload = {
iat: Math.floor(new Date() / 1000),
exp: Math.floor(new Date() / 1000) + 30,
sub: "api_key_jwt",
iss: "external",
jti: crypto.randomBytes(6).toString("hex")
};
return new Promise((resolve, reject) => {
jwt.sign(payload, privatekey, { algorithm: "RS256" }, function(
err,
token2
) {
if (err) reject(err);
else resolve(token2);
});
});
}
exports.genToken = async function() {
const header = {
"x-api-key": api
};
const data = {
kid: api,
jwt_token: await token()
};
async function authorization(req, res) {
try {
const auth = await rp({
url: authurl,
method: "POST",
headers: header,
body: data
});
res.send(auth.body);
} catch (error) {
res.send(404).send();
}
}
return {
"x-api-key": api,
Authorization: "Bearer " + authorization()
};
};
This works fine. Then I created a function to make the API call:
const token = require("./index").genToken;
const rp = require("request-promise");
exports.getOrderBook = function(res, error) {
const full_url = url + "order_book";
const auth = token();
rp({
url: full_url,
method: "GET",
headers: auth,
body: {
market: "btceur"
},
json: true
})
.then(function(response) {
res(response);
})
.catch(function(err) {
error(err);
});
};
And I call it using Express:
routes.get("/orderbook", async (req, res, next) => {
try {
const book = await orders.getOrderBook();
res.send(book);
} catch (error) {
next(error);
}
});
However, when I call my API, it shows an error in console:
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of
type string or Buffer. Received type object.
I guess the error is something with the token generation, because if I console.log(auth) in the getOrderBook function, it shows Promise { <pending> }, so probably an object is being passed as the jwt token.
Is it really the problem? I tried a lot of different solutions that I found on internet, however the concept of Async/Await is new to me, and I'm having some troubles to figure it out.
Thanks a lot in advance guys!
Since getToken is an anync function, the return is wrapped in a Promise as well so you would need another anync/await:
exports.getOrderBook = async function() {
let response;
try {
const full_url = url + "order_book";
const auth = await token();
response = await rp({
url: full_url,
method: "GET",
headers: auth,
body: {
market: "btceur"
},
json: true
});
} catch (e) {
// handle error
throw e
// or console.error(e)
}
return response;
};
In this line as well Authorization: "Bearer " + authorization(), authorization is returning a promise
const bearer = await authorization()
return {
"x-api-key": api,
Authorization: "Bearer " + bearer
};
For error handling wrap entire thing in try..catch block
exports.genToken = async function() {
try {
const header = {
"x-api-key": api
};
const data = {
kid: api,
jwt_token: await token()
};
async function authorization(req, res) {
let auth;
try {
auth = await rp({
url: authurl,
method: "POST",
headers: header,
body: data
});
// res object not available
// res.send(auth.body);
} catch (error) {
// res object not available, better throw error and handle in your middleware
// res.send(404).send();
}
return auth
}
const bearer = await authorization()
} catch (e) {
// handle error
}
return {
"x-api-key": api,
Authorization: "Bearer " + bearer
};
}