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);
});
}
Related
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.
I am new to Javascript and am just getting familiar with promises so forgive my ignorance.
What I'm trying to do is request all the records from an Airtable base and filter them them based on a checkbox called "Email Sent" being unsent. When I have the records filtered there are linked records in 2 fields that I need to do additional requests for to get the values for. All of those values (various data, and recipients as an object) then are plugged into an HTML email template and fired off using AWS-SES. Then the "Email Sent" check box is checked for those records to prevent emails from being sent multiple times. The plan is to have this all running on an interval in AWS Lambda. And as records are added to the base they are automatically emailed to a list.
I am able to get the records, and filter them. I am also comfortable with executing the code within Lambda and using SES. But I have been struggling for days to get the values for the linked records. Both of which are lists of people and 1 of which is a recipient email address.
The numerous things I've tried end up either returning the original record called in the first step or returns undefined. I'm chaining a lot of promises and I don't think I'm doing it right because it seems they are running out of order. I'm also at the point of trying weird things like using a global array instead of an object that I'm passing down through the promises and updating.
Any help is appreciated. Thank you in advance.
var Airtable = require('airtable');
var base = new Airtable({apiKey: 'xxxxxxxxxx'}).base('xxxxxxxxxx');
var nodemailer = require("nodemailer")
var handlebars = require('handlebars');
const path = require('path');
var AWS = require("aws-sdk")
var ses = new AWS.SES();
var fs = require('fs');
var mainArray = [];
var readHTMLFile = function(path, callback) {
fs.readFile(path, {encoding: 'utf-8'}, function (err, html) {
if (err) {
throw err;
callback(err);
}
else {
callback(null, html);
}
});
};
function getRecords(){
return new Promise(function(resolve, reject) {
var reqArr = [];
base('Edit Requests').select({
// Selecting the first 3 records in Grid view:
maxRecords: 50,
view: "Grid view"
}).eachPage(function page(records, fetchNextPage) {
// This function (`page`) will get called for each page of records.
records.forEach(function(record) {
// console.log("108: ", record._rawJson.id)
var obj = {}
// obj = record.fields;
// obj.id = record._rawJson.id;
console.log("172", record.fields["Editor Preference"])
obj = record.fields;
obj.id = record._rawJson.id;
if(record.fields["Editor Preference"] != undefined){
obj["Editor Preference"] = obj["Editor Preference"]
// obj["Editor Preference"] = getEditorWrap(record.fields["Editor Preference"])
} else {
obj["Editor Preference"] = "";
}
if(record.fields["Production Manager"] != undefined){
obj["Production Manager"] = obj["Production Manager"]
} else {
obj["Production Manager"] = "";
}
mainArray.push(obj)
// console.log(record.fields["Email"])
// console.log('Retrieved', record.fields['Requested By']);
})
// To fetch the next page of records, call `fetchNextPage`.
// If there are more records, `page` will get called again.
// If there are no more records, `done` will get called.
fetchNextPage();
// console.log("123", reqArr)
// resolve(reqArr)
}, function done(err) {
if (err) { console.error(err); return; }
// console.log("123", mainArray)
resolve(mainArray)
});
// resolve(reqArr)
});
}
function filterRecords(arr){
return new Promise(function(resolve, reject) {
var filtered = []
mainArray = [];
// console.log("245", arr)
for (i in arr){
if(arr[i]['Email Sent'] == undefined){
// console.log("247", arr[i])
mainArray.push(arr[i])
};
}
console.log("filtered: ", mainArray)
resolve(mainArray)
});
}
function setObject(array){
return new Promise(function(resolve, reject) {
for (i in array){
var obj = array[i];
if(obj.id != undefined){
base('Editors').find(obj.id, function(err, record) {
if (err) { console.error(err); return; }
// console.log("281", record.fields);
});
}
}
resolve(mainArray)
});
}
function main1(){
return new Promise(function(resolve, reject) {
getRecords().
then(function(recordArr){
filterRecords(mainArray).
then(function(resultArr){
setObject(mainArray).
then(function(mainArray){
})
})
})
});
}
main1().
then(function(resultArray){
console.log(resultArray)
})
I am trying to build login system using node.js and i am stuck in this call back function error i have managed to build get login info and check them with data base but when i am verifying password it's taking some time so in there i need to use call back function but even i used callback function it's giving me the same error which is since validation or database call taking time it's executing other stuff in the in my case if conditions.
i have tried to implement this another way just tried to console.log order and all executing opposite this my result first
this is the order that it's run
3
2
undefined
1
but i need run this exactly opposite should i use promises instead of callback?
const {ipcMain} = require('electron');
const Password = require("node-php-password");
const connection = require("./connection");
var hash;
var done;
var self = module.exports = {
getuser_information:function(user_name,pwd,callback){
connection.query("SELECT * FROM `super_admin` WHERE ad_un = ?", user_name, function(err, result, fildes) {
if (err) throw err;
let numbers_retuned = result.length;
hash = result[0].desk;
console.log(1);
});
callback(hash,self.true_or_not);
},
hashverif:function(hash,true_or_not){
true_or_not();
console.log(2);
},
true_or_not:function(){
console.log(3);
return 1
}
}
UPDATE after your comment
You have two errors
Your get_stored_password function returns nothing when your callback function is called that's why console.log(function_returning_nothing()) outputs undefined
You forgot to pass done to your callback function in
get_stored_password's definition callback(done)
const {
ipcMain
} = require('electron');
const Password = require("node-php-password");
const connection = require("./connection");
var hash;
var done;
var self = module.exports = {
get_stored_password: function(name, pwd, callback) {
connection.query("SELECT * FROM `super_admin` WHERE ad_un = ?", name, function(err, result, fildes) {
if (err) throw err;
let numbers_retuned = result.length;
hash = result[0].desk;
if (numbers_retuned == 1) {
var test = pwd;
done = Password.verify(test, hash);
} else {
console.log('no');
return 0;
}
//you must pass an argument to your callback function
// and return done var to get an output when you log this function
callback(done);
return done;
});
},
chek_if_true: function(done) {
console.log(done);
if (done) {
return true;
} else {
return false;
}
}
}
That's why when you log done to the console it's undefined
I have an api server and some script jobs. They are using the same function to pull a roster using mongoose and populate the players in the roster.
On the api server, this function is called normally. Using the script, it doesn't.
API example
function getRoster(id) {
var deferred = Q.defer();
Roster.find({_id:id}, 'playerRoster userId tournamentId').populate('playerRoster').exec(
function(err, roster) {
if (err) {
deferred.resolve(err);
}
deferred.resolve(roster[0]);
});
return deferred.promise;
}
api.post('/get_roster', function(req, res) {
// get tournament
var id = req.body._id;
var playerId = req.body.playerId;
getRoster(id).then(function(data) {
var roster=data;
res.json(roster);
});
});
Script
module.exports = function(config) {
this.getRoster=function(id) {
//return Roster.find({_id:id}, 'playerRoster userId tournamentId').exec( - THIS RETURNS
return Roster.find({_id:id}, 'playerRoster userId tournamentId').populate('playerRoster').exec(
function(err, roster) {
if (err) {
return err;
}
console.log('roster[0]',roster);
return roster[0];
});
}
this.tallyPoints = function(tournamentPlayer,sportsPlayers) {
var deferred = Q.defer();
var totalPoints =0;
console.log("tallyPoints 0 ",tournamentPlayer);
var rosterId = tournamentPlayer.player.roster[0];
console.log("tallyPoints 1 ",rosterId);
this.getRoster(rosterId).then(function(roster2){
console.log("tallyPoints 2 ",roster2);
...
deferred.resolve(totalPoints);
});
return deferred.promise;
}
return this;
};
In the script, neither logging for the roster[0] or tallyPoints 2 lines print, but there is no error either.
Why doesn't Roster.find return when I add populate? The only thing I can imagine is because playerRoster collection has 2000 records searching for ~10 and it hits some timeout that isn't being caught.
Any suggestion to clean it up is also appreciated.
Thanks
Moongoose supports promises for a long time. It's unsuitable to use callback-based Mongoose API where promises are desirable and the use of Q.defer with existing promises is known as Deferred antipattern (similarly, new Promise results in promise construction antipattern).
In its current state getRoster doesn't return a promise and doesn't handle errors correctly.
function getRoster(id) {
return Roster.find({_id:id}, 'playerRoster userId tournamentId').populate('playerRoster').exec()
.then(roster => roster[0]);
}
api.post('/get_roster', function(req, res) {
// get tournament
var id = req.body._id;
var playerId = req.body.playerId;
getRoster(id)
.then(function(data) {
var roster=data;
res.json(roster);
})
.catch(err => {
// handle error
});
});
Considering that only roster[0] is used, it likely should be changed to Roster.findOne.
It doesn't matter whether getRoster is used in Express route or elsewhere, it should work. It's unknown how module.exports = function(config) {...} module is used, but this may refer to wrong context if it isn't used as class. If getRoster and tallyPoints don't use config, they shouldn't reside inside this function.
I need to build an application that does these things (in order):
on load:
01- connect to MongoDB 'db'
02- creates a collection 'cas'
03- check if a web page has updates, if yes go to step 04, if not go to step 07
04- do web scraping (using Cheerio) of the web site and get a $ variable like that $ = cheerio.load(body);
05- elaborate this object to get only informations I'm interested in and organize them in a jsons object like this one:
var jsons = [
{year: 2015, country: Germany, value: 51},
{year: 2015, country: Austria, value: 12},
{year: 2016, country: Germany, value: 84},
{year: 2016, country: Bulgaria, value: 104},
...
];
06- insert each of these elements ({year: 2015, country: Germany, value: 51}, ...) in the collection 'cas' of database 'db'
07- download the data (for example in a csv file)
08- create a web page for data visualization of these data using D3.js
09- disconnect from 'db'
If Node.js were synchronous, I could write something like this:
var url = 'http://...';
var jsons = [];
connectDb('db');
createCollection('db', 'cas');
if(checkForUpdates(url)) {
var $ = scrape(url);
jsons = elaborate($);
for(var i = 0; i < jsons.length; i++) {
saveDocumentOnDbIfNotExistsYet('db', 'cas', jsons[i]);
}
}
downloadCollectionToFile('db', 'cas', './output/casData.csv');
createBarChart('./output/casData.csv');
disconnectDb('db');
But Node.js is asynchronous so this code would not work properly.
I've read that I can use Promise to get the code to run in a certain order.
I read the documentation about the Promise and some sites that showed simple tutorials.
The structure of a Promise is:
// some code (A)
var promise = new Promise(function(resolve, reject) {
// some code (B)
});
promise.then(function() {
// some code (C)
});
promise.catch(function() {
// some code (D)
});
// some code (E)
If I understood correctly, in this case the execution (if Node.js were synchronous) would be equivalent to:
// some code (A)
// some code (E)
if(some code (B) not produce errors) {
// some code (C)
}
else {
// some code (D)
}
or (swap between code A and E, because they are asynchronous)
// some code (E)
// some code (A)
if(some code (B) not produce errors) {
// some code (C)
}
else {
// some code (D)
}
So now I wonder what is the right structure for my application.
I thought about:
var cheerio = require('cheerio');
var express = require('express');
var fs = require('fs');
var MongoClient = require('mongodb').MongoClient;
var dbUrl = 'mongodb://localhost:27017/';
var dbName = 'db';
var collectionName = 'cas';
const app = express(); // run using > node app.js
// connect to db
var connect = function(url) {
return new Promise(function(resolve, reject) {
MongoClient.connect(url + dbName, function(err, db) {
if(err) {
reject(err);
}
else {
console.log('Connected');
resolve(db);
}
});
});
}
// create collection
connect.then(function(db) {
db.createCollection(collectionName, function(err, res) {
if(err) {
throw err;
}
else {
console.log('Collection', collectionName, 'created!');
}
});
});
// connection error
connect.catch(function(err) {
console.log('Error during connection...');
throw err;
});
It's right? If yes, how can I proceed with other steps?
I can I improve my code?
EDIT 1
Following the example of Андрей Щербаков, I modified my code in this way:
app.js:
// my files
var db = require('./middlewares/db.js');
var url = 'mongodb://localhost:27017/';
var dbName = 'db';
var collectionName = 'cas';
const start = async function() {
const connect = await db.connectToMongoDb(url, dbName);
const cas = await connect.createYourCollection(collectionName);
const isPageHasUpdates = oneMoreFunction(); // i don't know how you gonna check it
if(isPageHasUpdates) {
await step 4;
await step 5;
await step 6;
}
await step 7
return something; // if you want
}
start()
.then(res => console.log(res)) // here you can use result of your start function if you return something or skip this then
.catch(err => console.log(err)); // do something with your error
middlewares/db.js:
var MongoClient = require('mongodb').MongoClient;
let dbInstance;
var methods = {};
methods.connectToMongoDb = function(url, dbName) {
if(dbInstance) {
return dbInstance;
}
else {
MongoClient.connect(url + dbName, function(err, db) {
if(!err) {
dbInstance = db;
return db;
}
});
}
}
methods.createYourCollection = function(collectionName) {
?.createCollection(collectionName, function(err, res) {
if(err) {
throw err;
}
});
}
module.exports = methods;
But I'm not sure I'm doing well.
How can I separate function in different files? For example I want to put all the function about db in file middlewares/db.js. But I have some problems in line ?.createCollection(collectionName, function(err, res).
If you are running node version 7.6 or higher, better way will be to use async await which works with promises.
So your code will look like
const start = async() => {
const connect = await connectToMongoDb(url);
const cas = await connect.createYourCollection();
const isPageHasUpdates = oneMoreFunction(); // i don't know how you gonna check it
if(isPageHasUpdates) {
await step 4;
await step 5;
await step 6;
}
await step 7
return something; // if you want
}
start()
.then(res => console.log(res)) // here you can use result of your start function if you return something or skip this then
.catch(err => console.log(err)); // do something with your error
Sure any function you are gonna await should be promisified as you did with your connect function( but if you are using https://www.npmjs.com/package/mongodb functions already promisified)
Update
The best way will be to use mongoose, but if you want to work with native mongodb you can write your mongodb like this https://pastebin.com/BHHc0uVN (just an example)
You can expand this example as you want.
You can create function createCollection
const createCollection = (connection, collectionName) => {
return connection.createCollection(collectionName); // actually i'm not sure that this function exists in mongodb driver
}
And usage will be:
const mongodbLib = require('./lib/mongodb'); //path to db.js file
mongodbLib.init()
.then(connection => mongodbLib.createCollection(connection, 'cas'))
.then(() => doSmthElse())
Or if you are sure that init is done(you can do it once before you main script like starting server or whatever you doing)
const mongodbLib = require('./lib/mongodb'); //path to db.js file
const connection = mongodbLib.getConnection();
Or if you want to simple work with collection like in step 6, add your cas collection(like user in example file). But this you can use when your init function is done as well.
So usage will be
const mongodbLib = require('./lib/mongodb');
const cas = mongodbLib.collections.cas;
cas().insertMany(docs)
.then()
.catch()