Array is empty in the response even after pushing elements to it - javascript

I am creating backend APIs for a Twitter clone. The below API shows the profile of a user. I want to show the tweets of the people that this user is following. But in my response the tweets array is returning an empty array even though I have pushed the data into this array.
Can somebody help me understand why is the tweets array empty in the response?
app.get('/api/profile', auth, function (req, res) {
var email1 = req.user.email;
var followers_data = [];
var tweets = [];
follower.find({ emailoffollowee: email1 }, (err, results) => {
if (err)
console.log(err);
else {
results.map((d, k) => {
followers_data.push(d.emailoffollower);
})
for (var i = 0; i < followers_data.length; i++) {
Tweet.find({ author: followers_data[i] }, (err, results1) => {
if (err)
console.log(err);
else {
results1.map((d, k) => {
tweets.push(d);
})
}
})
}
res.json({
isAuth: true,
id: req.user._id,
email: req.user.email,
name: req.user.firstname + req.user.lastname,
followers: followers_data,
tweet: tweets
});
}
})
.catch(error => {
console.log(error);
})
});```

You Have Tweet.find() which is an asynchronous function, to resolve the problem I used async/await
app.get('/api/profile', auth, function (req, res) {
var email1 = req.user.email;
var followers_data = [];
var tweets = [];
follower.find({ emailoffollowee: email1 }, async (err, results) => {
if (err)
console.log(err);
else {
results.map((d, k) => {
followers_data.push(d.emailoffollower);
})
for (var i = 0; i < followers_data.length; i++) {
let results1 = await Tweet.find({ author: followers_data[i] });
results1.map((d, k) => {
tweets.push(d);
})
}
res.json({
isAuth: true,
id: req.user._id,
email: req.user.email,
name: req.user.firstname + req.user.lastname,
followers: followers_data,
tweet: tweets
});
}
})
.catch(error => {
console.log(error);
})
});
I recommend you to read this article async/await

Related

Node Js: Remove string array element from mongoDB

I have a user schema as follows:
const UserSchema = new mongoose.Schema({
skills: [String]
});
module.exports = mongoose.model("User", UserSchema);
And a Fetch request to delete a skill as follows:
const deleteItem = async (id) => {
try {
await fetch(`http://localhost:5000/api/user/deleteskill`, {
method: "DELETE",
headers: { "Content-Type": "application/JSON", token: accessToken },
body: JSON.stringify({ userid: userid , skill:id}),
})
.then((res) => res.json())
.then((data) => {
console.log("USER SKILLS:", data.userskills);
});
} catch (err) {
console.log(err);
}
};
Server
const deleteSkill = async (req, res) => {
try {
const user = await User.findById(req.body.userid)
//user.skills.pull(req.body.skill);
// removeskill = user.skills.filter(function(item) {
// return item !== req.body.skill
// })
if (user.skills.includes(req.body.skill)) {
res.status(400).json("Item Still Exists");
} else {
res.status(200).json("Item Deleted");
}
} catch (error) {
res.status(500).send({ error: error.message });
}
};
the array is in the following structure
[
'skill1', 'java', 'skill5'
]
I have tried to remove the user skill from the array in several ways but I still get res.status(400).json("Item Still Exists");. What I'm doing wrong?
Use the findOneAndUpdate method to find a document with the user id and update it in one atomic operation:
const deleteSkill = async (req, res) => {
try {
let message = "Item Deleted";
let status = 200;
const user = await User.findOneAndUpdate(
{ _id: req.body.userid },
{ $pull: { skills: req.body.skill } },
{ new: true }
)
if (user && user.skills.includes(req.body.skill)) {
message = "Item Still Exists";
status = 400;
} else if (!user) {
message = "User Not Found";
status = 404;
}
res.status(status).send({ message });
} catch (error) {
res.status(500).send({ error: error.message });
}
};
I believe you want to remove skills from the database then the following function could help you out.
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("mydb");
var myquery = { userid: userid, skillid: skillid};
dbo.collection("skills").deleteOne(myquery, function(err, obj) {
if (err) throw err;
console.log("1 document deleted");
db.close();
});
});
You have a method of removing elements from arrays, if you want to remove the first one you could use array.shift (more on it here), but if you want to delete it completely from your database you could always, find it and then update it.
User.update({ _id: userid }, { $pull: { "skills": "[skill]" }})

how to wait to store data in databases with nested loop in NodeJS

i want to fetch data from api and execute for loop to store all response data in mysql database.then again fetch data from api with different request parameter but it does not wait to complete for loop and store data i have tried async/await but noting works
app.get("/api/garudaTx", (req, res) => {
let sql = "SELECT * FROM table_name ORDER BY id ";
let query = conn.query(sql, (err, results) => {
(async function () {
for (let i = 0; i < results.length; i++) {
const element = results[i];
console.log(element.userAddress);
console.log(element.id);
try {
let response = await axios.get(
`https://api.bscscan.com/apimodule=account&action=txlist&address=${element.userAddress}&startblock=1&endblock={blockNo}&sort=asc&apikey=`
);
let last = await (async function () {
console.log(response);
if (response.status != 200 || response.data.result.length == 0) {
let code = response.status.toString();
fs.appendFile("responseError.txt", code + " ", (err) => {
if (err) throw err;
console.log("The lyrics were updated!");
});
fs.appendFile(
"responseError.txt",
element.userAddress + " ",
(err) => {
if (err) throw err;
console.log("The lyrics were updated!");
}
);
}
let body = response.data;
console.log(response.data.result.length);
const promises = [];
for (var index = 0; index < response.data.result.length; index++) {
let data = {
blockNumber: body.result[index].blockNumber,
timeStamp: body.result[index].timeStamp,
hash: body.result[index].hash,
nonce: body.result[index].nonce,
blockHash: body.result[index].blockHash,
from_address: body.result[index].from,
contractAddress: body.result[index].contractAddress,
to_address: body.result[index].to,
value: body.result[index].value,
transactionIndex: body.result[index].transactionIndex,
gas: body.result[index].gas,
gasPrice: body.result[index].gasPrice,
gasUsed: body.result[index].gasUsed,
cumulativeGasUsed: body.result[index].cumulativeGasUsed,
confirmations: body.result[index].confirmations,
};
promises.push(
new Promise((resolve) => {
let sql = "INSERT INTO table_name SET ?";
resolve(
conn.query(sql, data, (err, results) => {
if (err) throw err;
console.log(
JSON.stringify({
status: 200,
error: null,
response: results,
})
);
})
);
})
);
}
await Promise.all(promises);
})();
} catch (err) {
console.log(err);
}
}
})();
});
res.send(JSON.stringify({ status: 200, error: null, response: "success" }));
});
i am executing a for to fetch user details from database then i execute api for each user and saved response data with loop but next api is hitting before saving all data in database it does not wait to complete nested for loop

how push data to array with async nodejs?

i have a problem in nodejs. I want to push data from my sequelize query to array. this is my code
exports.getdoctorcategorybyid = async (req, res) => {
var id = req.params.id;
doctorcategory.findOne({
where: {
dokter_category_id: id
}
})
.then((doctorcategoryes) => {
if (doctorcategoryes) {
var l = [];
var dokter_l = doctorcategoryes.id_doctor.split(', ');
for(var b = 0; b < dokter_l.length; b++){
Dokterl.findOne({
where: {
id_doctor: dokter_l[b]
}
}).then((dokterl) => {
l.push(dokterl);
})
}
// data []
console.log(l)
res.status(200).json({
error: false,
response: "List!",
data: doctorcategoryes,
});
}else{
res.status(200).json({
error: true,
response: "not found!",
data: [],
});
}
})
};
i want to show data from variable l but result from variable l is []
exports.getdoctorcategorybyid = async (req, res) => {
var id = req.params.id;
doctorcategory.findOne({
where: {
dokter_category_id: id
}
})
.then(async(doctorcategoryes) => {
if (doctorcategoryes) {
var dokter_l = doctorcategoryes.id_doctor.split(', ');
var l= await Promise.all(dokter_l.map(async (item) => {
return await Dokterl.findOne({
where: {
id_doctor: item
}
});
}));
console.log(l)
res.status(200).json({
error: false,
response: "List!",
data: doctorcategoryes,
});
}else{
res.status(200).json({
error: true,
response: "not found!",
data: [],
});
}
}) };
You can try like this with Promise.all.

I am trying to get my site to show the amount of items in the basket at the basket icon on each page

I am building an e-commerce site with node.js and using EJS also, and i'm trying to get it so that the amount in the basket shows on each page. I have this function below that works as it shows the amount on one of the pages but not other pages.
function getBasketItems(req, res, callback) {
db.query('SELECT * FROM basketitems WHERE customerID = ?', [req.session.username], (err, res) => {
if (err) {
callback(err, null);
} else {
var amount = res.length;
callback(null, amount);
}
})
}
The function works here:
router.get('/pens', (req, res) => {
let title = 'Pens | Giraffe Website';
let header = 'Pens';
let sql = 'SELECT * FROM products';
let basketAmount = 0;
getBasketItems(req, res, (err, data) => {
if (err) {
console.log(err)
} else {
basketAmount = data;
}
});
db.query(sql, (err, result) => {
if (err) throw err;
res.render('products', {
title: title,
header: header,
data: result,
username: req.session.username,
loggedin: req.session.loggedin,
basketAmount: basketAmount
});
})
});
But not here:
router.get('/', (req, res) => {
let title = 'Home | Giraffe Website';
let basketAmount = 0;
getBasketItems(req, res, (err, data) => {
if (err) {
console.log(err)
} else {
basketAmount = data;
}
});
res.render('index', {
title: title,
username: req.session.username,
loggedin: req.session.loggedin,
basketAmount: basketAmount
});
})
Here is the GitHub Repo - https://github.com/shiney1884/giraffe-website
Any help would be greatly appreciated, thanks!
The fact that it works in the first case, as long as there are more then one connection to a DB, is a pure luck.
In order to get around this problem (and similar in the future) I think it would be good for you to first understand a concept of Javascript and asynchronous operations.
The basic tutorial can be found here https://www.javascripttutorial.net/javascript-callback/
Going through that, just imagine that setTimeout is a db.query.
The problem with the second code snippet is that is executes before the getBasketItems has returned a response since it executed synchronously. You can promisify getBasketItems as:
function getBasketItems(req, res) {
return new Promise((resolve, reject) => {
db.query('SELECT * FROM basketitems WHERE customerID = ?', [req.session.username], (err, res) => {
if (err) {
reject(err)
} else {
var amount = res.length
resolve(amount)
}
})
})
}
And then use it with async/await:
router.get('/', async (req, res) => {
let title = 'Home | Giraffe Website';
let basketAmount = 0;
let basketAmount = await getBasketItems(req, res)
res.render('index', {
title: title,
username: req.session.username,
loggedin: req.session.loggedin,
basketAmount: basketAmount
});
})
Hope that helps!

Using Multiple FindOne in Mongodb

I am trying to extend the amount of fields that our API is returning. Right now the API is returning the student info by using find, as well as adding some information of the projects by getting the student info and using findOne to get the info about the project that the student is currently registered to.
I am trying to add some information about the course by using the same logic that I used to get the project information.
So I used the same findOne function that I was using for Projects and my logic is the following.
I created a variable where I can save the courseID and then I will put the contents of that variable in the temp object that sending in a json file.
If I comment out the what I added, the code works perfectly and it returns all the students that I require. However, when I make the additional findOne to get information about the course, it stops returning anything but "{}"
I am going to put a comment on the lines of code that I added, to make it easier to find.
Any sort of help will be highly appreciated!
User.find({
isEnrolled: true,
course: {
$ne: null
}
},
'email pantherID firstName lastName project course',
function(err, users) {
console.log("err, users", err, users);
if (err) {
return res.send(err);
} else if (users) {
var userPromises = [];
users.map(function(user) {
userPromises.push(new Promise(function(resolve, reject) {
///////// Added Code START///////
var courseID;
Course.findOne({
fullName: user.course
}, function(err, course) {
console.log("err, course", err, course);
if (err) {
reject('')
}
courseID = course ? course._id : null
//console.log(tempObj)
resolve(tempObj)
}),
///// ADDED CODE END //////
Project.findOne({
title: user.project
}, function(err, proj) {
console.log("err, proj", err, proj);
if (err) {
reject('')
}
//Course ID, Semester, Semester ID
//map to custom object for MJ
var tempObj = {
email: user.email,
id: user.pantherID,
firstName: user.firstName,
lastName: user.lastName,
middle: null,
valid: true,
projectTitle: user.project,
projectId: proj ? proj._id : null,
course: user.course,
courseId: courseID
}
//console.log(tempObj)
resolve(tempObj)
})
}))
})
//async wait and set
Promise.all(userPromises).then(function(results) {
res.json(results)
}).catch(function(err) {
res.send(err)
})
}
})
using promise could be bit tedious, try using async, this is how i would have done it.
// Make sure User, Course & Project models are required.
const async = require('async');
let getUsers = (cb) => {
Users.find({
isEnrolled: true,
course: {
$ne: null
}
}, 'email pantherID firstName lastName project course', (err, users) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
let findCourse = (users, cb) => {
async.each(users, (user, ecb) => {
Project.findOne({title: user.project})
.exec((err, project) => {
if (!err) {
users[users.indexOf(user)].projectId = project._id;
ecb();
} else {
ecb(err);
}
});
}, (err) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
let findProject = (users, cb) => {
async.each(users, (user, ecb) => {
Course.findOne({fullName: user.course})
.exec((err, course) => {
if (!err) {
users[users.indexOf(user)].courseId = course._id;
ecb();
} else {
ecb(err);
}
});
}, (err) => {
if (!err) {
cb(null, users);
} else {
cb(err);
}
});
};
// This part of the code belongs at the route scope
async.waterfall([
getUsers,
findCourse,
findProject
], (err, result) => {
if (!err) {
res.send(result);
} else {
return res.send(err);
}
});
Hope this gives better insight on how you could go about with multiple IO transactions on the same request.

Categories

Resources