Firebase: can't write Username + Title to DB [duplicate] - javascript

So I'm following a Savvy Apps tutorial in order to learn Vue.js. This tutorial uses Firebase with Firestore. Since Firestore is in Beta (as the tutorial says), changes might happen - and I think that might be the case here.
In any case, I'm trying to sign up a new user. I fill out the form and click 'Sign up' and I get this error message:
Error: Function CollectionReference.doc() requires its first argument to be of type string, but it was: undefined
But looking in Firebase, I see that the user has been created. So why do I get this error message? What is the first argument?
The code for signup looks like this:
signup() {
this.performingRequest = true;
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password).then(user => {
this.$store.commit('setCurrentUser', user);
// create user obj
fb.usersCollection.doc(user.uid).set({
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile');
this.performingRequest = false;
this.$router.push('/dashboard')
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
},
Let me know if you need more code - this is the first time I'm testing Vue.js.

createUserWithEmailAndPassword() returns a Promise containing a UserCredential. UserCredential has a property user for the firebase.User object.
You need to make the appropriate changes to your code to correctly access the UID:
signup() {
this.performingRequest = true;
fb.auth.createUserWithEmailAndPassword(this.signupForm.email, this.signupForm.password)
.then(credential=> { // CHANGED
this.$store.commit('setCurrentUser', credential.user); // CHANGED
// create user obj
fb.usersCollection.doc(credential.user.uid).set({ //CHANGED
name: this.signupForm.name,
title: this.signupForm.title
}).then(() => {
this.$store.dispatch('fetchUserProfile');
this.performingRequest = false;
this.$router.push('/dashboard')
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
}).catch(err => {
console.log(err);
this.performingRequest = false;
this.errorMsg = err.message
})
},

Related

How can I fix double running function in firebase and react project?

I am trying to check existing of data on firebase real-time database and then add new data.
When I add exist data, it works well.
When I add new data, it works two time and don't add new data.
registerStaff = async (model) => {
if (!firebase.apps.length) {
return false;
}
if (model) {
return new Promise((resolve, reject) => {
this.db.ref("tbl_phone_number").on("value", async (snapshot) => {
if (snapshot.exists()) {
const samePhone = await _.filter(snapshot.val(), (o) => {
`find same phone number`;
return o.phone.toString() === model.phone.toString();
});
console.log("checking...", snapshot.val());
if (samePhone.length > 0) {
`checking samephone Number`;
console.log("exist...");
`if exist, return error`;
resolve({
type: "phone",
message: "The phone number is already used.",
});
} else {
`If there is no, add new phone number`;
const newPostKey = this.db
.ref()
.child("tbl_phone_number")
.push().key;
this.db
.ref(`tbl_phone_number/${newPostKey}`)
.set({ phone: model.phone, type: model.type })
.then(() => {
console.log("making...==>");
resolve({
type: "success",
message: "Successfully registered.",
});
})
.catch((err) => {
resolve({
type: "phone",
message: "Sorry. Something went wrong",
});
});
}
}
});
});
}
};
//console log result checking... checking... exist... checking... exist... making...
I found solution to solve this problem in using function "on" and "once".
'on' function of firebase is used to keep data from firebase and add new data automatically when I add new data.
'once' function of firebase is used to change data only once.
Therefore in above question, if I use 'once' function instead of 'on', it will work well.
Good luck.

How can I check If a value of a Boolean Variable is false?

I don't know If I'm checking for the value of the boolean correctly
what this code does: the user creates a note for himself, his ID is on the note and it needs to belong to a category name that has to be in the category schema ( where my error happens )
exports.postAddNote = (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
const error = new Error("validation failed, entered data is incorrect");
throw error;
}
const content = req.body.content;
const tags = req.body.tags;
const categoryName = req.body.categoryName;
let creator;
const note = new Note({
content: content,
categoryName: categoryName, // work
tags: tags,
creator: req.userId,
});
Category.find()
.select("-_id")
.select("-__v")
.select("-notesId")
.then((categories) => {
console.log(categories); //stripping everything but names off categories
const CategoryExists = categories.some(
(category) => category.name === categoryName
);
console.log(CategoryExists); // ~~~~~~~~~~ this logs correctly
if (CategoryExists === -0) { // ~~~~~~~~~~ what i want: if the value is false
return res.json({ Error: "The category you entered does not exist" });
}
note // ~~~~~~~~~~ the code stops here :/ it doesn't save the note
.save()
.then((note) => {
console.log("saved note");
User.findById(req.userId);
})
.then((user) => {
creator = user;
user.notes.push(note);
return user.save();
})
.then((result) => {
res.status(201).json({
info: {
dateCreated: new Date().toISOString(),
status: "Note Created Successfully",
creator: { _id: creator._id, email: creator.email },
},
});
})
.catch((err) => {
if (!err.statusCode) {
err.statusCode = 500;
}
});
})
.catch((err) => {
console.log(err);
next();
});
};
if (CategoryExists === -0)
should be
if (CategoryExists === false)
or just
if (!CategoryExists)
i believe. did you try that? not sure why you are using -0. the return value for some() is either going to be true or false.
try this:
if (!CategoryExists) {
return res.json({ Error: 'The category you entered does not exist' });
}

How to deal with race conditions in nodejs

I'm trying to work on a MERN full stack app where the frontend sends an api call to "/createDeckOfCards" to my nodejs backend. The goal is to click on a button to create a new deck of cards, then return the list of created cards.
The parameter numOfCards is sent with this call as well.
So on my nodeJS backend, I have the "/createDeckOfCards" endpoint where I use .map() to iteratively create each card and then save to mongoDB like so:
const allCardsArray = [...Array(req.body.numOfCards).keys()]
allCardsArray.map(async (i)=>{
const eachCard = new eachCardModel({
eachCardTitle: String(i)
})
eachCard.save((err, doc) => {
if (err) return res.status(400).json({ errMsg: "Something went wrong" });
else{
CardDeckModel.findOneAndUpdate(
{_id: req.cardDeckCreated._id},
{$push:{allCards: doc}},
function(error, success){
if (error){
console.log(error)
return res.status(400).json({ errMsg: "Something went wrong" });
} else {
console.log("success")
}
}
)
}
});
})
console.log("COMPLETED") //DOES NOT EXECUTE LAST!
//THIS RETURNS BEFORE THE .map() is done
res.status(200).json({
createdCardDeckID: req.cardDeckCreated._id
})
})
After that, I have a second endpoint "/returnAllCardsInDeck" where I pass in the ID of the cardDeck like so:
CardDeckModel.findOne({_id: req.body.createdCardDeckID}).populate({path: 'allCards', options: { sort: "eachCardTitle" } }).exec((err, cardDeck) => {
if (err) return res.status(400).json({ errMsg: "Something went wrong" });
else {
res.status(200).json({
CardDeck: cardDeck
})
}
})
The problem is, CardDeck returns before the allCardsArray.map() is completed. This would be a problem because I want the user to see ALL cards in the deck once the deck is created. But because the "/returnAllCardsInDeck" executes before the "/createDeckOfCards", it returns be an undefined object.
Also, am I doing this right? Esp with regards to the first part ("/createDeckOfCards").
try this, you can't do async call like this with map. There are patterns to solve this issue. Promise.all is one of them.
const allCardsArray = [...Array(req.body.numOfCards).keys()]
await Promise.all(
allCardsArray.map((i)=>{
const eachCard = new eachCardModel({
eachCardTitle: String(i)
})
return eachCard.save()
.then(
() =>
CardDeckModel.findOneAndUpdate({_id: req.cardDeckCreated._id}, {$push:{allCards: doc}})
.then(() => console.log("success"))
.catch((error) => console.log(error) || res.status(400).json({ errMsg: "Something went wrong" });)
).catch(() => res.status(400).json({ errMsg: "Something went wrong" }))
})
)
console.log("COMPLETED") //DOES NOT EXECUTE LAST!
//THIS RETURNS BEFORE THE .map() is done
res.status(200).json({
createdCardDeckID: req.cardDeckCreated._id
})
})
you can use for of with async/await instead of map like this
const allCardsArray = [...Array(req.body.numOfCards).keys()];
for (let i of allCardsArray) {
const eachCard = new eachCardModel({
eachCardTitle: String(i),
});
try {
let doc = await eachCard.save();
await CardDeckModel.findOneAndUpdate(
{ _id: req.cardDeckCreated._id },
{ $push: { allCards: doc } }
);
} catch (error) {
return res.status(400).json({ errMsg: "Something went wrong" });
}
}
console.log("success");
res.status(200).json({
createdCardDeckID: req.cardDeckCreated._id,
});

Avoid duplicates when saving new data with mongoose

I am working on an application where I can save destinations to my Mongo DB. I would like to throw a custom error when trying to save a destination that already exsist in the DB. Mongoose prevents that from happening but I want clear and userfriendly error handling.
// post a new destination
router.post('/',
(req, res) => {
const newCity = new cityModel(
{
name: req.body.name,
country: req.body.country
}
)
newCity.save()
.then(city => {
res.send(city)
})
.catch(err => {
res.status(500).send('Server error')
})
});
Before saving a new destination, you can check if there is document already using findOne method, and if it exists you can return a custom error.
router.post("/", async (req, res) => {
const { name, country } = req.body;
try {
const existingDestination = await cityModel.findOne({name,country});
if (existingDestination) {
return res.status(400).send("Destionation already exists");
}
let newCity = new cityModel({ name, country });
newCity = await newCity.save();
res.send(city);
} catch (err) {
console.log(err);
res.status(500).send("Server error");
}
});
Note that I guessed the duplication occurs when the same country and name exist. If it is not what you want, you can change the query in findOne.
Since you've created unique index, When you try to write duplicate then the result would be :
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }"
}
})
Your code :
Constants File :
module.exports = {
DUPLICATE_DESTINATION_MSG: 'Destionation values already exists',
DUPLICATE_DESTINATION_CODE: 4000
}
Code :
//post a new destination
const constants = require('path to constants File');
router.post('/',
(req, res) => {
const newCity = new cityModel(
{
name: req.body.name,
country: req.body.country
}
)
try {
let city = await newCity.save();
res.send(city)
} catch (error) {
if (error.code == 11000) res.status(400).send(`Destination - ${req.body.name} with country ${req.body.country} already exists in system`);
/* In case if your front end reads your error code &
it has it's own set of custom business relates messages then form a response object with code/message & send it.
if (error.code == 11000) {
let respObj = {
code: constants.DUPLICATE_DESTINATION_CODE,
message: constants.DUPLICATE_DESTINATION_MSG
}
res.status(400).send(respObj);
} */
}
res.status(500).send('Server error');
})

Bluebird with mongoose using Promise.Each

I'm stuck in a function I'm working with ( I can be doing this all wrong ). So a quick explanation, I want to add bulk data in a collection, the collection is called "Sites" the format of the CSV is site,country,type. I'm trying to use promises for this (Bluebird). So consider the code:
Promise.each(sites, sites => new Promise((resolve, reject) => {
//console.log(sites);
let name = tools.extractDomain(req, res, sites[0]);
let country = sites[1];
let group = sites[2];
if (name != "" && country != "" && group != "") {
Site.findOne({ name: name }, "_id", function(err, duplicate) {
if (false) {
console.log("Duplicate site: " + duplicate);
} else {
//console.log("Adding " + name)
let site = new Site()
site.name = name
site.meta = {}
site.group = group
site.country = country
site.geomix = []
site.addedBy = req.user._id
site.addedAt = Date.now()
site.saveAsync().then(function(response){
tools.saveHistory(req, res, response._id, response.name, "Website Meta fetched.");
tools.saveHistory(req, res, response._id, response.name, "Link added for the first time."); //Save in history
resolve(site);
}).catch(function (e){
console.log(name);
reject();
});
}
});
}else{
console.log('Wrong Format');
}
}).then((data) => {
console.log('All websites processed!');
addedSites.push(data);
}).catch(err => {
//console.error('Failed');
}));
res.send({ status: 'ok', message: ''});
I'm making ajax calls so I return a res.send({ status: 'ok', message: ''}), I know that its in the incorrect place and I want to send some data along the res.send. Currently it sends the headers before the code actually finishes. I want to send the headers after all the data is added in Mongo but for every each in this case he resolve() so if I send the headers inside the ".then" of the ".each" I will get headers already sent error.
This might be a bit confusing. I feel I'm not doing this right. I'm going a bit crazy as well as I can't find a proper example that I can understand and implement.
But in the end my main question is: using an Ajax call what's the proper way to add let's say 1000 records in a collection using promises and actually control properly those who fail to add and those who don't?
Right now my code actually works but the logic is wrong for sure.
Thanks.
You can use bulkWrite on your model.
Ref: http://mongoosejs.com/docs/api.html#model_Model.bulkWrite
EDIT:
Sorry I misunderstood you. You need to move res.send({ status: 'ok', message: ''}); to then() and catch() blocks, so you will get something like this:
Promise.each(sites, sites => new Promise((resolve, reject) => {
// stuff you did before
}).then((data) => {
console.log('All websites processed!');
addedSites.push(data);
res.send({ status: 'ok', message: ''});
}).catch(err => {
res.send({ status: 'failed', message: err.message});
}));
This is what I came too, if someone can tell me if this is a good arch.
exports.addBulkSite = function(req, res, next) {
let siteArray = csv.parse((req.body.sites).trim()),
addedSites = [],
failedSites = [],
duplicated = [],
sites = siteArray,
size = sites.length,
processed = 0,
meta;
Promise.each(sites, sites => new Promise((resolve, reject) => {
let name = tools.extractDomain(req, res, sites[0]),
country = sites[1],
group = sites[2];
if (name != "" && country != "" && group != "") {
Site.findOneAsync({ name: name }, "_id").then(function(duplicate) {
duplicated.push(duplicate);
reject({name:name, message: 'Duplicated', critical:false});
}).catch(function(notDuplicated){
let site = new Site()
site = {
name: name,
meta: {},
group: group,
country: country, geomix:{},
addedBy: req.user._id,
addedAt:Date.now()
}
site.saveAsync().then(function(response){
tools.saveHistory(req, res, response._id, response.name, "Website Meta fetched.");
tools.saveHistory(req, res, response._id, response.name, "Link added for the first time."); //Save in history
resolve(site);
}).catch(function (e){
console.log(e);
reject({name:name, message: 'Error saving in the database. Please contact the administrator.', critical: true});
});
});
}else{
reject({name:name, message: 'Paramaters are missing', critical:false});
}
}).then((data) => {
processed++;
addedSites.push(data);
if(processed==size){
console.log('out');
res.send({ status: 'ok', addedSites: addedSites, failedSites: failedSites, duplicated: duplicated});
}
}).catch((err) => {
processed++;
console.log(err);
failedSites.push(err);
if(processed==size){
console.log('out');
res.send({ status: 'ok', addedSites: addedSites, failedSites: failedSites, duplicated: duplicated});
}
}));
}

Categories