I'm new to Parse and trying to wrap my head around promises at the same time. What I'd like to do is simply create a user in the DB if one doesn't exist and if one does exist I would update the row. The problem I am having is that my Parse.Query never finds the user row and then when it fails to find the row, it then fails to create a new user because the row exists. I know this is because either I need to force the method to wait for the search to return or to return a promise to be executed when the query returns, but I cannot figure out how to do it.
My current code is:
Parse.Cloud.define("findUser2", async req => {
var phoneNumber = req.params.phoneNumber;
phoneNumber = phoneNumber.replace(/\D/g, '');
console.log("Incoming phone number is: " + phoneNumber)
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('username', phoneNumber);
var user = await userQuery.first();
if(user) {
console.log("Found a user, user is: " + user);
//Validation stuff goes here
} else {
console.log("Did not find a user, create and return it");
var newUser = new Parse.User();
newUser.setUsername(phoneNumber);
newUser.setPassword(secretPasswordToken + phoneNumber);
newUser.set("language", "en");
newUser.setACL({});
newUser.save();
//If this is not commented out, fails with:
//Error: Cannot create a pointer to an unsaved ParseObject
//user = newUser;
}
console.log("about to return the user");
return user;
});
The output then returns:
app[web.1]: Incoming phone number is: 5555551212
app[web.1]: Did not find a user, create and return it
app[web.1]: about to return the user
app[web.1]: info: Ran cloud function findUser2 for user undefined with:
app[web.1]: Input: {"phoneNumber":"5555551212"}
app[web.1]: Result: undefined {"functionName":"findUser2","params":{"phoneNumber":"5555551212"}}
app[web.1]: error: Parse error: Account already exists for this username. {"code":202,"stack":"Error: Account already exists for this username.\n at /app/node_modules/parse-server/lib/RestWrite.js:567:13\n at processTicksAndRejections (internal/process/task_queues.js:85:5)"}
app[web.1]: (node:23) UnhandledPromiseRejectionWarning: Error: Account already exists for this username.
Based on the ordering of the logs, I know it is exiting the function before it completes the query. I thought the await
would force it to not continue until this query returns or times out, thereby being able to fulfill the promise. It seems like I am wrong though and I am unsure how to resolve the issue.
Ideally I'd use Promises because I'd allow my code to run more efficiently, but I have await in for now because I was hoping it would make it simpler to get it working and be fine for a prototype. I'm looking for a good path forward and if the right approach is to return a promise, I'd like to know.
Your cloud code function is returning before the new user is actually saved. You just need to await
for it before returning. It would be something like this:
Parse.Cloud.define("findUser2", async req => {
var phoneNumber = req.params.phoneNumber;
phoneNumber = phoneNumber.replace(/\D/g, '');
console.log("Incoming phone number is: " + phoneNumber)
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('username', phoneNumber);
var user = await userQuery.first({ useMasterKey: true });
if(user) {
console.log("Found a user, user is: " + user);
//Validation stuff goes here
} else {
console.log("Did not find a user, create and return it");
var newUser = new Parse.User();
newUser.setUsername(phoneNumber);
newUser.setPassword(secretPasswordToken + phoneNumber);
newUser.set("language", "en");
newUser.setACL({});
await newUser.save(); //This is the line you need to change
//If this is not commented out, fails with:
//Error: Cannot create a pointer to an unsaved ParseObject
//user = newUser;
}
console.log("about to return the user");
return user;
});