Promise Pending airtable - node.js

I'm working with the airtable API, node js
I have a table called mentors
I have a second table called jobs
I use a find function which will return the job name for each mentor
I return records so I could pass it to my view
I got promise pending. r
router.get('/',function(req, res, next) {
const title= 'test'
const getRecords = async () => {
const records = await mentors
.select({maxRecords: 6, view: "Grid view"})
.eachPage(function page(records, fetchNextPage) {
records.forEach(function(records) {
jobs.find(records.fields["Quel est votre métier actuel"], function(err, record) {
if (err) { console.error("Big Error",err); return; }
records.fields.job = record.fields.Job;
return records
});
});
});
fetchNextPage();
}
getRecords()
.then(function(records) {
console.log(records)
res.render('index',{title: title, records});
});
});

Related

GET endpoint returns a 404

I am trying to retrieve specific user data from my Postgres DB.
This code, which retrievs all user data works:
app.get("/employees", async (req, res) => {
try {
const allEmployees = await pool.query("SELECT * FROM employees");
res.json(allEmployees.rows);
} catch (err) {
console.error(err.message);
}
});
But this code meant to retrieve one user doesn't. It returns a 404 on Postman.
app.get("/employees/:id", async (req, res) => {
try {
const { id } = req.params;
const oneEmployee = await pool.query("SELECT * FROM employees WHERE emp_id = $1", [
id
]);
res.json(oneEmployee.rows[0]);
} catch (err) {
console.error(err.message);
}
});
I don't seem to figure out what the problem is.
#AnujPancholi an update. I used the node-postgres queries documentation and changed my code as follows:
app.get("/employees/:emp_id", async (req,res) => {
const query = {
// give the query a unique name
name: 'fetch-user',
text: 'SELECT * FROM employees WHERE emp_id = $1'
}
query.values = [req.params.emp_id];
// callback
await pool.query(query, (err, response) => {
if (err) {
console.log(err.stack);
} else {
res.json(response.rows);
}
});
});
then on Postman my endpoint to a GET route
http://localhost:3000/employees/4
I did not enter any values on the params section. Thanks for pointing me in the right direction, especially on the Postman part.

How to handle async when making mongoose query in each array element in express?

On the following post method, I'm having some issues due to moongose async. res.send(suggestions) is executed first then Expense.findOne.exec
app.post('/suggestions', async function(req, res) {
const suggestions = await req.body.map((description) => {
Expense.findOne({ description: new RegExp(description, 'i') }).exec((err, result) => {
if (result) {
console.log(result.newDescription);
return {
description,
newDescription: result.newDescription,
category: result.category,
subcategory: result.subcategory
};
}
});
});
res.send(suggestions);
});
The result is a array of null values. How can I executed a query for each item, then execute res.send(suggestion)?
Found solution with the following code:
app.post('/suggestions', async function(req, res) {
try {
if (req.body.length > 0) {
const suggestions = req.body.map((description) =>
Expense.findOne({ description: new RegExp(description, 'i') })
);
const results = await Promise.all(suggestions);
return res.send(results);
}
} catch (e) {
console.log('error', e);
}
});

Nested queries, promises on nodejs

Im trying to build a rest api, fetching a nested mysql queries.
When i fetch the first query, this return a array, then with this array i need to fetch data with another query for each value through a array.map
when the script running, always log a empty array, i think must be cause of promises. any help please?
//this the mysql queries
const getTournaments = 'SELECT ID FROM wp_posts WHERE post_type = "tournament"'
const getTournamentGame = 'SELECT meta_value FROM wp_postmeta WHERE meta_key = "tournament_game" AND post_id = ?'
async function fetchType(id){
return new Promise ((res, rej) => {
try{
pool.query(getTournamentGame, [id], (err, rows) => {
if (err) {
return rej(err)
}else {
return res(rows[0].meta_value)
}
})
} catch(err){
console.log(err)
}
})
}
async function mapeado(array) {
return new Promise (async (resolve,rej) => {
try{
var arr = []
array.map((item) => {
fetchType(item.ID).then((res) => {
var tourData = {
id: item.ID,
type: res
}
return tourData
}).then((data) => {
arr.push(data)
})
})
return resolve(arr)
} catch(err) {
console.log(err)
}
})
}
//making rest api
app.get('/tournaments', async (req, res) => {
pool.query(getTournaments, (err, rows) => {
mapeado(rows).then(console.log)
})
})

Aysnc for each callback issue

I am using async for each to achieve some task the problem i am facing is that the final call back never executes
Scenario : i have list of contacts and want to send message to all contacts in parallel and when message is send want to store the response in array and than want to perform some action on final call back
sms.js
function SmsService() {}
SmsService.prototype.sendSms = function(value, callback) {
client.messages
.create({
body: value.body,
from: value.from,
to: value.to
})
.then(message => {
console.log('meesage going', message.sid);
callback(null,message.sid)
})
.catch(e => {
callback(null,'not send')
})
}
module.exports = SmsService;
sender.js
var SmsService = require(path.resolve(__dirname, './sms'));
var smsService = new SmsService();
var data = [{body:'1232324',from:'+12323123',to:'+12312323'},
{body:'112123234',from:'+123123123',to:'+123213123'}, {body:'12sadasdasd34',from:'+112123123',to:'+1223213123'}]
async.forEachOf(data, function (value, i, cb) {
console.log('started',i)
smsService.sendSms(value, function(error, result) {
console.log('sending',i,value.to)//only get result for first item
results.push(result)
cb()
})
}, function (err) {
if (err) console.error(err.message);
console.log('all done')//never executes
console.log(results);//never executes
});
If I move the async part to SMS service it works fine but I want to keep separate the SMS service
You can try something like this.
sms.js
function SmsService() {}
SmsService.prototype.sendSms = function(value, callback) {
return new Promise((resolve, reject) => {
// Do async job
client.messages
.create({
body: value.body,
from: value.from,
to: value.to
})
.then(message => {
console.log('meesage going', message.sid);
resolve(callback(null,message.sid))
})
.catch(e => {
reject(callback(null,'not send'))
})
})
}
module.exports = SmsService;
sender.js
var SmsService = require(path.resolve(__dirname, './sms'));
var smsService = new SmsService();
var data = [{body:'1232324',from:'+12323123',to:'+12312323'},
{body:'112123234',from:'+123123123',to:'+123213123'},
{body:'12sadasdasd34',from:'+112123123',to:'+1223213123'}];
var promises = [];
data.forEach(async function (obj, index) {
console.log('started',index)
promises.push(await smsService.sendSms(obj, function(error, result) {
console.log('sending',i,value.to)//only get result for first item
results.push(result)
}));
});
Promise.all(promises).then(function () {
console.log("Messages sent")
}).catch(function (err) {
console.log("Messages not sent")
})

Express/NodeJS multiple mysql queries for the same route

In a blog made in express/nodejs I am trying to display in the single article page both the article (working fine) and a set of 2 recommended articles. Unfortunately as you can see in the commented bit of the code it doesn't work (can't render the same template twice)
What would be the best approach in this case?
<!-- language: lang-js -->
router.get('/*', function(req, res, next) {
var slug=req.url.replace(/^\//,'').replace(/\/$/,'');
var bg = getRandomInt(5);
if(slug==''){
connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', function (error, results, fields) {
res.render('index', { title: title, year: year, bg:bg, pages: results });
});
}else{
connection.query('SELECT * FROM `pages` where slug=?', [slug], function (error, results, fields)
{
if(results.length){
res.render('page', { title: results[0].title, page:results[0] });
}else{
console.log(req);
res.render('error',{url: 'http://'+req.headers.host+req.url});
}
});
/* not working apparently you can't send the header of the template twice umhh
connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2', function (error, random, fields)
{
res.render('page', { pages:random});
});
*/
}
});
The way you have it now
Both queries will complete (and call their callback) at unrelated times
res.render will be called multiple times, which does not work because it assumes all of the data is being sent in a single call. So it sends the HTTP headers, which cannot be sent twice.
Updated according to what it looks like you intended. Note this makes the order of the queries sequential, which may not be desirable. You'll want to use the async lib to help manage running them both at the same time and still consolidate the results:
router.get('/*', (req, res, next) => {
const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
const bg = getRandomInt(5);
if (slug == '') {
return connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', (error, results, fields) => {
res.render('index', { title: title, year: year, bg: bg, pages: results });
});
} else {
return connection.query('SELECT * FROM `pages` where slug=?', [slug], (error, results, fields) => {
if (results.length) {
return connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2', (error, random, fields) => {
if (error) {
// handle error
}
// consolidate renders into a single call
// adjust the template file accordingly
return res.render('page', { title: results[0].title, page: results[0], pages: random });
});
} else {
console.log(req);
return res.render('error', { url: 'http://' + req.headers.host + req.url });
}
});
}
});
Alternatively, consider using bluebird & async/await, this is just another style - to give you options that are new based on node 8+. In this one the queries are kicked off at the same time again.
const bluebird = require('bluebird');
router.get('/*', async (req, res, next) => {
try {
const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
const bg = getRandomInt(5);
if (slug == '') {
const results = await bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` WHERE NOT slug = "about"', cb));
return res.render('index', { title: title, year: year, bg: bg, pages: results });
} else {
const [results, random] = await Promise.all([
bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` where slug=?', [slug], cb)),
bluebird.fromCallback(cb => connection.query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2', cb))
]);
if (results && results.length) {
return res.render('page', { title: results[0].title, page: results[0], pages: random });
} else {
return res.render('error', { url: 'http://' + req.headers.host + req.url });
}
}
} catch (e) {
return res.render('error', { url: 'http://' + req.headers.host + req.url });
}
});
You can't render a page twice, otherwise you'll get Error: Can't set headers after they are sent to the client
What you need to do, is fetch the current article and the recommended pages, and render the page once you have the results from both queries.
In order to achieve that I used: Promise.all, and then performed a single res.render
router.get('/*', async (req, res, next) => {
const slug = req.url.replace(/^\//, '').replace(/\/$/, '');
const bg = getRandomInt(5);
if (slug == '') {
const results = await query('SELECT * FROM `pages` WHERE NOT slug = "about"');
return res.render('index', {
title: title,
year: year,
bg: bg,
pages: results
});
}
// Get current article and recommended pages
// Promise.all returns an array where each entry has
// the resolved value of the promise passed at that index
const [article, recommended] = await Promise.all([
query('SELECT * FROM `pages` where slug=?', [slug]),
query('SELECT * FROM `pages` ORDER by RAND () LIMIT 2')
]);
if (article.length) {
// Render the article & recommended pages at once.
res.render('page', {
title: article[0].title,
page: article[0],
pages: recommended
});
} else {
console.log(req);
res.render('error', {
url: 'http://' + req.headers.host + req.url
});
}
});
// Query helper
// You can use promisify...
function query(statement, placeholders = []) {
return new Promise((resolve, reject) => {
connection.query(query, placeholders, (error, results) => {
if(err)
return reject(err);
resolve(results);
});
});
}

Resources