I want to pass multiple query objects with res.render() inside my route.js. I have a select.js which contains the SQL statements and delivers the objects to my route.js. This works fine until I want to pass multiple query objects with res.render().
Any ideas on how I can pass multiple objects at once?
snippet route.js (I need to pass get_PriceData here as well)
I already query get_KategorieData but I have no clue how to handle multiple queries in one route.
router.get('/edit', (req, res, next) => {
var speisenEintragenData = {};
db.get_KategorieData()
.then(({ data: kategorie_data }) => {
res.render('speiseEintragen', { kategorie_data }); //maybe putting res.render() after the db.get?
})
.catch((error) => {
console.log(error);
});
});
select.js
const db = require('./config');
//KATEGORIEN LADEN
const get_KategorieData=()=>{
var sql = 'SELECT * FROM Kategorie';
return new Promise((resolve,reject) => {
db.query(sql, function (err, data, fields) {
if (err) reject(err);
resolve({data});
});
})
}
//PREISE LADEN
const get_PriceData=()=>{
var sql = 'SELECT * FROM preise';
return new Promise((resolve,reject) => {
db.query(sql, function (err, data, fields) {
if (err) reject(err);
resolve({data});
});
})
}
module.exports={
get_KategorieData,
get_PriceData
}
There are two ways to go about this. One is to stick with promises and other is to use async/await.
Using promise
Create a new function to query database. This is if the module you are using does not support async/await and requires a callback.
const query = ( sql ) => {
return new Promise(( resolve, reject) => {
db.query(sql, function (err, data, fields) {
if (err) reject(err);
resolve(data);
});
})
}
// and then you can write an async/await function to call n queries like
const get_data = async () => {
const sql1 = '...';
const a = await query( sql1 );
const sql2 = '...';
const b = await query( sql2 );
....
....
....
const sqln = '...';
const n = await query( sqln );
return { a ,b,... ,n};
}
Or with async/await you can directly call db.query and use the response
const get_data = async () => {
const sql1 = '...';
const res_1 = await db.query(sql1);
const sql2 = '...';
const res_2 = await db.query(sql2);
return { a: res_1 ,b: res_2 };
}
router.js can rewritten as
router.get('/edit', async (req, res, next) => {
const {a:rename_a,b:rename_b and so on}=await db.get_data();
res.render('view', { rename_a,rename_b and so on })
});
Related
I am attempting to retrieve the JSON data stored in the following API:
https://api.hatchways.io/assessment/blog/posts
Using node.js and https requests, I constantly receive an array of [ Promise { }, Promise { } ]. Unfortunately, I can only search by one tag at a time, and I have to retrieve a list of posts that has at least one tag from a list of provided tags, before sorting the posts. My code is below:
const express = require('express');
const app = express();
app.get("/api/ping", (req, res) => {
res.status(200).send("{\"success\": true}");
})
app.get("/api/posts", (req, res) => {
const tags = req.query.tags;
const sortBy = req.query.sortBy;
const direction = req.query.direction;
if (!tags) res.status(400).send("Must provide at least one tag.");
let tag_array = tags.split(',');
let posts_array = [];
tag_array.forEach(tag => {
let posts = getPost(tag);
posts_array.push(posts);
})
console.log(posts_array);
})
app.listen(3000, () => console.log("Listening on port 3000..."));
function getPost(tag) {
const https = require('https');
return new Promise( (resolve, reject) => {
const options = {
hostname: 'api.hatchways.io',
path: `/assessment/blog/posts?tag=${tag}`
}
let body = [];
const req = https.request(options, res => {
res.on('data', data => {
body.push(data);
});
res.on('end', () => {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch (error) {
reject(error);
}
resolve(body);
});
});
req.on('error', error => {
reject(error);
});
req.end();
}).then(function(data) { return data; }, function(error) { console.log(error) });
}
the getPost method is returning a promise, just do this:
app.get("/api/posts", async (req, res) => {
const tags = req.query.tags;
const sortBy = req.query.sortBy;
const direction = req.query.direction;
if (!tags) res.status(400).send("Must provide at least one tag.");
let tag_array = tags.split(',');
const promises = [];
tag_array.forEach(tag => {
promises.push(getPost(tag))
});
posts_array = await Promise.all(promises)
console.log(posts_array)
})
Just wait for all promises to resolve using the await keyword.
app.get("/api/posts", async (req, res) => { // Add the async keyword
//... Skipped some code for concision
tag_array.forEach(tag => {
let posts = getPost(tag);
posts_array.push(posts);
})
await Promise.all(posts_array) // Add this to await all promises to resolve
console.log(posts_array);
})
This is confusing me a lot, because I'm repeating the code structure I have on other parts of my project (and it works there).
This is the part where it doesn't work:
/utils/socketQueries.js
const db = require('../db')
exports.addMessage = async (message) => {
console.log('add message func', message)
try {
/* const res = await db.query('select 1', (err, res) => {
console.log(res)
})*/
const res = await db.query('select 1') // this doesn't work either
return res
} catch (error) { console.error('adding message error', error) }
}
This is called from /socketserver.js
exports.socketServer = (wss) => {
let socket_id = new Map()
wss.on("connection", (ws, req) => {
ws.on('message', (data) => { //messages from the client
parseMessage(ws, socket_id, data)
})
})
...
this is how I set up the db connection in db.js
require('dotenv').config()
const { Pool } = require('pg')
const connectionString = process.env.DBCONNSTRING
const db = new Pool({
connectionString: connectionString,
})
module.exports = db;
and an example of a working query function would be /api/health.js
const db = require('../db');
exports.check = async (req, res) => {
let response = {
apiResponsive: true
}
try {
dbResponsive = await db.query(`select 1`)
if (dbResponsive.rows[0]) response = {dbResponsive: true, ...response}
else response = {dbResponsive: true, ...response} }
catch(error) {
console.error('error on db health check', error)
response = {dbResponsive: 'error while checking', ...response}
}
res.json(response)
}
I've been trying different ways to run the query: .then, callback, async/await and it always returns either the unresolved promise or undefined - it never throws an error.
Can anyone sort out whats the (likely very basic) error I'm missing?
EDIT: Trying out the answers proposed I noticed that if I try an INSERT query, nothing is inserted on the DB. regardless of what I do with what db.query returns, the query should run.
I have 3 tables (services, practitioners, network) in my postgres database and I want them all to show in my API, but I got this error
(node:21300) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
and this is the only output I could get
json response
here is my code.
const handler = (req, res, db, tableName) => {
try{
db.select('*').from(tableName)
.then(data => {
if(data.length){
res.status(200).json({tableName: data});
}
})
}catch(err){
res.status(400).json(err)
}
}
const content = (req, res, db) => {
handler(req, res, db, 'services')
handler(req, res, db, 'practitioners')
}
module.exports = { content };
edit:
here's what I did from Nabil Farhan's answer, and it's working just what I wanted. screenCapture
const getData = async (db, tableName) => {
return new Promise((resolve, reject) => {
db.select('*').from(tableName)
.then(data => {
resolve({[tableName]:data})
})
});
}
const contentHandler = async (req, res, db) => {
// get the argument from get request
let allTable = [];
const table_a = await getData(db, 'services');
const table_b = await getData(db, 'practitioners');
allTable.push(table_a);
allTable.push(table_b);
res.status(200).json({data: allTable});
}
module.exports = { contentHandler };
I recommend you to use promise like this:
async function getData(tableName) {
return new Promise(function (resolve, reject) {
db.select("*")
.from(tableName)
.then((data) => {
resolve(data);
});
});
}
async function run(){
var allTable = [];
const firstTable = await getData('testingDB');
const secondTable = await getData('user');
allTable.push(firstTable);
allTable.push(secondTable);
res.status(200).json({data: allTable});
}
run().then(() => {
console.log("Finished!");
})
I see a couple potential issues here.
Unhandled Promise rejection:
Add a catch block after then (you can rethrow to put it in the existing catch block).
.then(data => {
if(data.length){
let responseData = {}
responseData[tableName] = data
res.status(200).json(responseData)
}
})
.catch(err) {throw err}
tableName is also not going to be interpreted as variable, but as a literal property name, so I changed it to get what I think you were going for.
For the header error, you are setting the response twice, once for "services", then again for "practitioners". One possible solution is to remove the res.status... line from handler and use Promise.all in content and moving the response setting there:
const content = (req, res, db) => {
Promise.all(
handler(req, res, db, 'services')
handler(req, res, db, 'practitioners')
)
.then(allData => {
//use reduce/Object.assign to combine the individual data objects
allData.reduce((finalResponseData, data) => {
if(!data.length) return finalResponseData
Object.assign(finalResponseData, data)
})
.then(finalResponseData => res.status(200).json(finalResponseData)
.catch(err) {res.status(400).json(err)}
}
So I have a MySQL query that I need to call within another MySQL query in the following format:
var sql = "some sql code";
const queryResult = await new Promise((resolve) => {
connection.query(sql, (err, rows, fields) => {
// do some stuff here
var secondSql = "Another sql command";
const queryTwo = await new Promise((resolve) => {
connection.query(secondSql, (err, rows, fields) => {
// do some more stuff
resolve(something);
)});
resovle(something);
}
)});
But, when I try to run this, I keep getting the error:
"errorMessage": "await is only valid in async function",
"errorType": "SyntaxError",
I know this has something to do with the "await new Promise((resolve) => {" not being async, but how do I fix this to actually run? I previously would just insert an async function outside of this code and call it where I have the second SQL query, but that does not work in AWS Lambdas for some reason.
Could someone please show me another way of doing this?
In order for await to work, it has to be called from inside an async function. Try this:
var sql = "some sql code";
const queryResult = await new Promise((resolve) => {
connection.query(sql, async (err, rows, fields) => {
// do some stuff here
var secondSql = "Another sql command";
const queryTwo = await new Promise((resolve) => {
connection.query(secondSql, (err, rows, fields) => {
// do some more stuff
resolve(something);
)});
resolve(something);
}
)});
You could try the approach:
const sql1 = 'some sql code';
const sql2 = 'Another sql command';
const query1 = () => {
return new Promise((resolve) => {
connection.query(sql1, (err, rows, fields) => {
resolve('something')
});
})
};
const query2 = () => {
return new Promise((resolve) => {
connection.query(sql2, (err, rows, fields) => {
resolve('something')
});
})
};
[query1, query2].forEach(async request => {
await request();
});
I am trying to compile a list of all customers using the Stripe Node API. I need to make continual fetches 100 customers at a time. I believe that I need to use a Promise within the API call to use async await, but I can't for the life of me figure out where to put it. Hoping to make this gist public use and I want to get it right, thanks.
getAllCustomers()
function getMoreCustomers(customers, offset, moreCustomers, limit) {
if (moreCustomers) {
stripe.customers.list({limit, offset},
(err, res) => {
offset += res.data.length
moreCustomers = res.has_more
customers.push(res.data)
return getMoreCustomers(customers, offset, moreCustomers, limit)
}
)
}
return customers
}
async function getAllCustomers() {
const customers = await getMoreCustomers([], 0, true, 100)
const content = JSON.stringify(customers)
fs.writeFile("/data/stripe-customers.json", content, 'utf8', function (err) {
if (err) {
return console.log(err);
}
console.log("The file was saved!");
});
}
IF res in stripe.customers.list({limit, offset}).then(res => ...) is the same as res in the "callback" version of stripe.customers.list({limit, offset}, (err, res) - then you probably could rewrite your code like
const getMoreCustomers = limit => {
const getThem = offset => stripe.customers.list({limit, offset})
.then(res => res.has_more ?
getThem(offset + res.data.length).then(result => res.data.concat(...result)) :
res.data
);
return getThem(0);
};
async function getAllCustomers() {
const customers = await getMoreCustomers(100);
const content = JSON.stringify(customers);
fs.writeFile("/data/stripe-customers.json", content, 'utf8', function (err) {
if (err) {
return console.log(err);
}
console.log("The file was saved!");
});
}
additional to Jaromanda X's answer, it seems there is no offset option to customers api, but starting_after https://stripe.com/docs/api/node#list_customers
So, getMoreCustomers may be fixed like
const getMoreCustomers = starting_after => {
const getThem = starting_after => stripe.customers.list({limit: 100, starting_after: starting_after})
.then(res => res.has_more ?
getThem(res.data[res.data.length - 1]).then(result => res.data.concat(...result)) :
res.data
);
return getThem(starting_after);
};
async function getAllCustomers() {
const customers = await getMoreCustomers(null);
const content = JSON.stringify(customers);
fs.writeFile("/data/stripe-customers.json", content, 'utf8', function (err) {
if (err) {
return console.log(err);
}
console.log("The file was saved!");
});
}