Fetching all stripe customers with async await - javascript

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!");
});
}

Related

Await sql and push to array before executing the result

I'm using mysql npm package to get data from the database.
My goal is to add every list(id) from lists Table and push it into lists array.
I'm getting the correct data from the database but when I result the query lists array is empty.
I think that I have to add async and await to the code to make it work. Tried in several places but I didn't make it work. Any idea what I'm doing wrong?
// GET - get all grocery_lists
Grocery_list.getAll = (result) => {
let lists = []; // <--- List Array
sql.query("SELECT id FROM lists", (err, res) => { // <--- Get all id from 'lists' table
if (err) {
console.log(err);
}
res.forEach(list => { // <--- Loop thru all lists
sql.query(`
SELECT items.id, items.name, items_x_lists.purchased
FROM items_x_lists
INNER JOIN items ON items_x_lists.itemId = items.id
WHERE items_x_lists.listId = ${list.id};
`, (err, res) => { // <--- Get all items for ${list.id} from 'items' table
if (err) {
console.log(err);
}
const list = {};
list.id = res.id;
console.log(list); // <--- { id: 1 } ... { id: 2 }
lists.push(list);
});
});
result(null, lists); // <--- returning empty array instead of [{ id: 1 }, { id: 2 }]
});
};
I think you can do it simply like this (not the exact code) but I think you've got the idea:
// GET - get all grocery_lists
Grocery_list.getAll = (result) => {
getData().then(data => {
// call result(data);
}).catch(err => {
// handle error for no data or any other errors
})
}
const getData = async () => {
try {
var res = await sql.query("SELECT id FROM lists");
// Handle error if no data found
if (!res) { Promise.reject("No data found"); }
} catch (error) {
return Promise.reject(error);
}
const listIds = res.map(id => id); // array of ids
try {
var data = await sql.query(`
SELECT items.id, items.name, items_x_lists.purchased
FROM items_x_lists
INNER JOIN items ON items_x_lists.itemId = items.id
WHERE items_x_lists.listId in ${listIds};`);
// Handle error if no data found
if (!data) { return Promise.reject("No data found") }
} catch (error) {
return Promise.reject(error);
}
return Promise.resolve(data)
}
this mighht be the solution
Grocery_list.getAll = async (result) => { return sql.query("SELECT id FROM lists", (err, res) => { // <--- Get all id from 'lists' table if (err) { console.log(err); } }); };
I’m outside, so I can only edit with my mobile phone. If the layout is strange, I apologize for it.
First of all, if it is me, I will adjust a few places
1.Create an async function and use Promise.all technology
async function queryAll(querys){
return await Promise.all(querys);
}
2.Create a Promise function to execute each sql
const queryPromise = id => {
return new Promise((resolve, reject) => {
sql.query(
'SELECT items.id, items.name, items_x_lists.purchased FROM items_x_lists INNER JOIN items ON items_x_lists.itemId = items.id WHERE items_x_lists.listId = ? ;',
id,
(err, rows, fields) => {
console.log(rows);
if (err) reject(err);
else resolve(rows);
}
);
});
};
3.Adjust the internal logic of the getAll event
const querys = []
res.forEach(list => {
querys.push(queryPromise(list.id));
});
const querysResults = await queryAll(querys);// remember to use async for function
querysResults.forEach(querysResult => {
lists.push({id:querysResult['id']});
});
Because there is no computer on hand, there may be some situations that need to be judged, but roughly this will work normally.
Hope this helps you :)

Passing multiple query objects with res.render

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 })
});

TypeError: query.findAll is not a function node js mysql

i am trying to pass the sql query parameters to data access js file. i have imported the function in current file, but still am getting the below error.
current file
const tcount = async (value) => {
const sql = 'trainingcount';
const result = await query.findAll(sql);
return result;
}
data access file
const query = (results) => {
findAll: async (sql, result) => {
connection.query(`SELECT * FROM trainingcount`, (err, rows) => {
if (err) {
return results(null, err);
} else {
return results(rows);
}
});
};
};
export { query };
(node:11132) UnhandledPromiseRejectionWarning: TypeError:
query.findAll is not a function
EDIT: Check #rid solutions on the comments for the specific problem of calling the proper function. My answer solves a different problem in OP code.
you call return inside the callback function, so you are returning THAT function, not findAll. You need to return a Promise:
const query = (results) => {
findAll: (sql, result) => {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM trainingcount`, (err, rows) => {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
};
};
export { query };

Javascript get returned value of inside functions

I have the following function that gets a username and by making a query to mssql checks if already exists in databse. I want the checkForUsername() to return true if recordSet.recordset.length >= 1. How can I do that? I am a bit confused about async/await. Thanks.
function checkForUsername(UserIn) {
var dbConn = new sql.ConnectionPool(config);
dbConn.connect().then(function () {
var request = new sql.Request(dbConn);
request.query("SELECT * from Admins WHERE username='"+UserIn+"';").then(function (recordSet) {
console.log(recordSet.recordset.length);
if(recordSet.recordset.length >= 1)
//checkForUsername return true
dbConn.close();
}).catch(function (err) {
dbConn.close();
});
}).catch(function (err) {
console.log(err);
});
}
Of course I can't check if the following code works, but you can try rewriting your code for something like this:
const checkForUsername = async user => {
try {
const
dbConn = new sql.ConnectionPool(config),
sqlObj = await dbConn.connect(),
query = `SELECT * FROM Admins WHERE username='${user}';`, // Note that creating a query explicitly is not recommended as it is vulnerable to SQL injection attack!
recordSet = await new sqlObj.Request(dbConn)query(query),
hasRecords = !!recordSet.recordset.length;
return hasRecords;
} catch (error) {
console.log(error);
} finally {
dbConn.close();
}
}
// Call it
(async () => {
console.log(await checkForUsername('Jhon due');
})();
const checkForUsername = async UserIn => {
try {
var dbConn = new sql.ConnectionPool(config);
const sqlObj = await dbConn.connect();
const recordSet = await new sql.Request(dbConn).query(`SELECT * FROM Admins WHERE username='${UserIn}';`);
const hasRecords = !!recordSet.recordset.length;
return hasRecords;
}catch (error) {
console.log(error);
}finally{
dbConn.close();
}
}
// Call it
(async () => {
console.log(await checkForUsername('John'));
})();

Javascript how the better way to code nested callback?

I have 3 layer callbacks like this :
app.post('/', (req, res) => {
var filename = `outputs/${Date.now()}_output.json`;
let trainInput = req.files.trainInput;
let trainOutput = req.files.trainInput;
let testInput = req.files.trainInput;
//first
trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`, function (err) {
if (err) return res.status(500).send(err);
//second
trainOutput.mv(`inputs/${req.body.caseName}/train_output.csv`, function (err) {
if (err) return res.status(500).send(err);
//third
testInput.mv(`inputs/${req.body.caseName}/test_input.csv`, function (err) {
if (err) return res.status(500).send(err);
res.send('success');
});
});
});
});
In this case, there are only 3 file uploads. In another case, I have more than 10 file uploads, and it makes 10 layer callbacks. I know it because of JavaScript asynchronous.
Is there any way, with this case, to make a beautiful code? This is because when it 10 layer callbacks, the code looks horizontally weird.
Thanks
You can use the following code to make you code look better and avoid callback hell
app.post('/', async (req, res) => {
var filename = `outputs/${Date.now()}_output.json`;
let trainInput = req.files.trainInput;
let trainOutput = req.files.trainInput;
let testInput = req.files.trainInput;
try {
var result1 = await trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`);
var result2 = await trainInput.mv(`inputs/${req.body.caseName}/train_output.csv`);
var result2 = await testInput.mv(`inputs/${req.body.caseName}/test_input.csv`);
res.send('success');
}
catch (error) {
res.status(500).send(error);
}
});
You can make the functions return a Promise
I advice to make one function because you do the same thing 3 times. In this case I called the function 'save' but you can call it what ever you want. The first parameter is the file end the second the output filename.
function save(file, output) = return new Promise((resolve, reject) => {
file.mv(`inputs/${req.body.caseName}/${output}`, err =>
if (err) return reject(err)
resolve()
})
Promise.all([
save(req.files.trainInput, 'train_input.csv'),
save(req.files.trainInput, 'train_output.csv'),
save(req.files.trainInput, 'test_input.csv')
])
.then(_ => res.send(200))
.catch(err => res.send(400);
What version of Node you using? If async/await is available that cleans it up a bunch.
const moveCsv = (file, dest) => {
return new Promise((resolve, reject) => {
//third
file.mv(dest, function (err) {
if (err) reject(err);
resolve();
});
})
}
app.post('/', async(req, res) => {
try {
var filename = `outputs/${Date.now()}_output.json`;
const {
trainInput,
trainOutput,
testInput
} = req.files;
const prefix = `inputs/${req.body.caseName}`;
await moveCsv(trainInput, `${prefix}/train_input.csv`);
await moveCsv(trainOutput, `${prefix}/train_output.csv`);
await moveCsv(testInput, `${prefix}/test_input.csv`);
res.send('success');
} catch(err) {
res.status(500).send(err);
}
});
I'm also assuming here that your trainInput, trainOutput, testOutput weren't all meant to be req.files.trainInput.
Just be careful since the synchronous nature of the await calls are thread blocking. If that writer function takes ages you could also looking at putting those calls onto a worker thread. Won't really matter if your requests to that server endpoint are fast and non-frequent.
You can add RXJS to your project and use Observables.forkJoin()
Solution with Observables(assuming that trainInput.mv() returns Observable):
/* Without a selector */
var source = Rx.Observable.forkJoin(
trainInput.mv(`inputs/${req.body.caseName}/train_input.csv`),
trainInput.mv(`inputs/${req.body.caseName}/train_output.csv`),
trainInput.mv(`inputs/${req.body.caseName}/test_input.csv`)
);
var subscription = source.subscribe(
function (x) {
// On success callback
console.log('Success: %s', x);
},
function (err) {
// Error callback
console.log('Error');
},
function () {
// Completed - runs always
console.log('Completed');
});
// => Success: [result_1, result_2, result_3] or Error
// => Completed

Categories