Check if document already exists if not create one - node.js

Im learning expressjs + mongo. I want to check after user logs in with passport through Steam if his data is already in database if not to create a record for him.
For this I created a static method in schema. Unfortunatelly i can't save from the inside of it.
TypeError: Object # has no method 'create'
SteamAccountSchema.statics.checkAccount = function(identifier){
this.findOne({ 'identifier' : identifier }, function(err, account){
if(err) throw err;
console.log("Checking account:" + account)
if(account) {
console.log("user already in db")
return true
} else {
console.log("Creating new user account")
this.create({
name : 'username',
identifier: identifier
}, function(err){
if(err) throw err;
// if (err) return done(err);
return false
});
}
});
}

Just cache the this object. I.e. in the code below self points to what you need:
SteamAccountSchema.statics.checkAccount = function(identifier){
var self = this;
this.findOne({ 'identifier' : identifier }, function(err, account){
if(err) throw err;
console.log("Checking account:" + account)
if(account) {
console.log("user already in db")
return true
} else {
console.log("Creating new user account")
self.create({
name : 'username',
identifier: identifier
}, function(err){
if(err) throw err;
// if (err) return done(err);
return false
});
}
});
}

Related

How to compare input and Document in mongoDB

When I Register [ input Email / password to DB ] success. Then, I want to login. If input[email/pass] == document in collection >> go to next page, else console.log['wrong email/pass']
I try to wirte IF/else code but I don't know check condition.
This code is Register form
app.post('/register',function(req,res){
MongoClient.connect(url, function(err, db) {
if (err) throw err;
let dbo = db.db("project");
let myobj = { Email: req.body.email, Password: req.body.psw } ;
dbo.collection("Register").insertOne(myobj, function(err, res) {
if (err) throw err;
console.log(" document inserted");
db.close();
});
});
});
This code is Login form
app.post('/index',function(req,res){
MongoClient.connect(url, function(err, db) {
if (err) throw err;
let dbo = db.db("project");
let cursor = dbo.collection('Register').find();
cursor.each(function(err,doc) {
if (doc == req.body.email && req.body.psw){
console.log("goto next page");
}
else{
console.log('wrong');
}
});
db.close();
});
});
Correct input and wrong input Output is = Wrong
Pls insert loop check all of array pls.
app.post('/index',function(req,res){
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("project");
dbo.collection("Register").findOne({}, function(err, result) {
if (result.Email == req.body.email && result.Password == req.body.psw) {
console.log("OK");
}
else{
console.log(result.Email && result.Password);
}
db.close();
});
});
});
You have to compare individual values, like so:
if (doc.Email == req.body.email && doc.Password == req.body.psw){
console.log("goto next page");
}
Firstly you should check for valid request body and the you should do a fineone query instead of running a for-loop and checking. see the corrected one below :
app.post("/index", function(req, res) {
let {
email,
psw
} = req.body;
if (email && psw) {
console.log("wrong credentials");
return;
} else {
MongoClient.connect(url, function(err, db) {
if (err) throw err;
let dbo = db.db("project");
let data = dbo.collection("Register").findOne({
Email: email,
Password: psw
});
if (data) {
console.log("goto next page");
} else {
console.log("wrong");
}
db.close();
});
}
});
I'm late to the party but I just found a solution to a similar problem and wanted to share.
If you have input values in javascript and want to use them in a mongodb query you need to make them in to strings.
Assuming user._id is a value coming from a javascript function call.
This will work:
{ userId: { $eq: ${user._id} } } ✅
This won't work:
{ userId: { $eq: user._id } } ❌

Setting a function callback fram an async request

Good Morning All,
I have been looking for an answer to this on the boards, but my noob brain just can't make sense of it.
i have this function in models/user.js
module.exports.getUserByUsername = function(username, callback){
var retUser = new User;
sql.connect(dbConfig, function(err) {
if (err) {
console.log(err);
callback();
}
// create Request object
var request = new sql.Request();
request.input('ip_username', sql.NVarChar, username)
// query to the database and get the records
request.query('select * from [portal_users] where username = #ip_username', function(err, recordset) {
if (err) {
console.log(err);
return;
} else {
var user = new User(recordset.recordset[0].username,recordset.recordset[0].password,recordset.recordset[0].email,recordset.recordset[0].name);
user.addID(recordset.recordset[0].id);
retUser = user;
}
callback();
// send records as a response
//res.send(recordset);
});
});
function callback() {
sql.close();
return retUser;
};
}
and this code in my routes/user.js
passport.use(new LocalStrategy(
function(username, password, done) {
User.getUserByUsername(username, function(err, user){
if(err) throw err;
if(!user){
return done(null, false, {message: 'Unknown User'});
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
});
}));
I have been modifying an example from GITHUB that uses mongoDB for the DB connection, but I would like to use MS SQL. The function is successfully calling the database and returning the correct values. However I don't know how to initiate the callback so I can pass the retUser object back to the original function for processing and logging in.
I did for a moment try to do this by not using the callback and using a standard return type of function, however I quickly realised that given the async nature this wouldn't work.
any help here would be greatly appreciated.
Thanks
OK I managed to figure it out using this post:
Node.js npm mssql function returning undefined
my new code is:
module.exports.getUserByUsername = function(username, callback){
var connection = new sql.ConnectionPool(dbConfig, function(err) {
if (err) {
console.log(err);
callback(err);
return
}
// create Request object
var request = new sql.Request(connection);
request.input('ip_username', sql.NVarChar, username)
// query to the database and get the records
request.query('select * from [portal_users] where username = #ip_username', function(err, recordset) {
if (err) {
console.log(err);
callback(err,recordset);
return;
} else {
var user = new User(recordset.recordset[0].username,recordset.recordset[0].password.replace(/ /g,''),recordset.recordset[0].email,recordset.recordset[0].name);
user.addID(recordset.recordset[0].id);
callback(err,user);
}
sql.close();
// send records as a response
//res.send(recordset);
});
});
}

Send variables to layout

I'm learning how to build web applications using Node.js and express, so I'm really noob yet.
So, I have some questions here. I'm building a landing page, and all the informations that I'm getting from my Database (in mysql) will appear in a single page.
I'm sending values from my database, to my layout, built in Jade.
And I created multiple functions to get specific data, here an example:
function getUser(username, userId, callback) {
connection.query('SELECT * FROM users WHERE user_id = ?', userId, function(err, result) {
if (err)
callback(err, null);
else
var callBackString = {};
callBackString.value1 = result[0].user_email;
callBackString.value2 = result[0].user_name;
callback(null, callBackString);
});
}
When the user tries to login I check if the user exists to change the layout and send to the layout some important values:
router.post('/login', function(req, res) {
connection.query('SELECT user_id FROM users WHERE user_email = ? AND user_password = ?', [req.body.login, req.body.password], function(err, results) {
if (err) throw err;
if (results[0]) {
userId = results[0].user_id;
getUser("username", userId, function(err, data) {
if (err) {
console.log("ERROR : ", err);
} else {
res.render('logged_in', {
email: data.value1,
username: data.value2,
});
res.end();
}
});
} else {
res.render('index', {
validation: "failed"
});
}
});
});
I'm only calling one function here (getUser()), and when I call this function, the layout changes, and I send some values.
But now I would like to create a new function called getPosts(), to get informations from a different table, and send it to the layout too, like I did when i called the function getUser()
I tried to do something like this but I had no success, when I call the variables outside the scope I keep getting "undefined".
router.post('/login', function(req, res) {
connection.query('SELECT user_id FROM users WHERE user_email = ? AND user_password = ?', [req.body.login, req.body.password], function(err, results) {
if (err) throw err;
if (results[0]) {
userId = results[0].user_id;
getUser("username", userId, function(err, data) {
if (err) {
console.log("ERROR : ", err);
} else {
email = data.value1;
username = data.value2;
}
});
getPosts("posts", 1, function(err, data) {
if (err) {
console.log("ERROR : ", err);
} else {
postName = data.value1;
postText = data.value2;
}
});
res.render('logged_in', {
email: email,
username: username,
pstname: postName,
psttxt: postText
});
res.end();
} else {
res.render('index', {
validation: "failed"
});
}
});
});
What do I need to change on my code? Thank you.
You should read about asynchronization in node.js so if you change your code as bellow it may work:
router.post('/login', function(req, res) {
connection.query('SELECT user_id FROM users WHERE user_email = ? AND user_password = ?', [req.body.login, req.body.password], function(err, results) {
if (err) throw err;
if (results[0]) {
userId = results[0].user_id;
getUser("username", userId, function(err, data) {
if (err) {
console.log("ERROR : ", err);
} else {
email = data.value1;
username = data.value2;
getPosts("posts", 1, function(err, data) {
if (err) {
console.log("ERROR : ", err);
} else {
postName = data.value1;
postText = data.value2;
res.render('logged_in', {
email: email,
username: username,
pstname: postName,
psttxt: postText
}
});
}
});
} else {
res.render('index', {
validation: "failed"
});
}
});
});

Validating email and username in clean style

I need to validate a username (if one is provided) and an email (if one is provided) before saving to the db. I'm using MongooseJS, However, I'm not sure how to structure my code
Here is what I have so far:
var user = new User();
if(req.body.email) {
User.findOne({"email" : req.body.email}, function(err, found){
if(err) return next(err);
if(found) return res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Email address already exists"});
});
}
if(req.body.username) {
User.findOne({"username" : req.body.username}, function(err, found){
if(err) return next(err);
if(found) return res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Username already exists"});
});
}
user.save(function(err){
if(err) return next(err);
res.status(200).send(user);
});
but of course this won't work as the user.save will be executed before either of the validation blocks execute. I realise I could put user.save inside the callbacks but then I would be repeating code which I want to avoid.
The best thing to do is to add validation on to your mongoose Schema and let it do it automatically whenever you attempt to save a model (or you can call the validation function yourself earlier). Like so:
var userSchema = new Schema({
email: {
type: String,
required: true,
validate: {
validator: function(v) {
var emailRegexp = /^[-a-z0-9~!$%^&*_=+}{\'?]+(\.[-a-z0-9~!$%^&*_=+}{\'?]+)*#([a-z0-9_][-a-z0-9_]*(\.[-a-z0-9_]+)*\.(aero|arpa|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro|travel|mobi|[a-z][a-z])|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,5})?$/i;
return emailRegexp.test(v);
},
message: "{VALUE} does not appear to be a valid email address."
}
}
});
Then when you try to save it with incorrect data:
var user = new User({ email: "this_isnt_a_proper_email" });
user.save(function(err) {
if (err.name === "ValidationError") { // check that it comes from mongoose validation
console.log(err.errors.email.message)
res.status(400).send({ validationError: err }); // send "Bad Request" HTTP header
} else {
res.send(user) // status 200 is implicit when not set
}
});
To better organise the code that checks whether the username or email is already set in the database, I'd suggest looking up Bluebird (or similar) Promises, so that you can have a logical flow to the process.
Another simple way maybe
var user = new User();
var email = req.body.email || '';
var username = req.body.username || '';
User.find({
$or: [{"email": email}, {"username": username}]},
function(err, users){
if(err) return next(err);
if(users && users.length == 0) {
// save new user if none is matched
user.save(function(err){
if(err) return next(err);
res.status(200).send(user);
});
} else if (users && users.length > 0) {
// check users returned to determine which of following two error codes should be returned
//return res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Email address already exists"});
//return res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Username already exists"});
}
});
You could do it through Promise, here is one sample codes by using Q.js
function findUserByEmail() {
var deferred = Q.defer();
if(req.body.email) {
User.findOne({"email" : req.body.email}, function(err, found){
if(err) return deferred.reject(err);
if(found) {
res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Email address already exists"});
deferred.reject();
}else {
// no user is found, resolve it
deferred.resolve();
}
});
} else {
deferred.reject();
}
return deferred.promise;
}
function findUserByName() {
var deferred = Q.defer();
if(req.body.username) {
User.findOne({"username" : req.body.username}, function(err, found){
if(err) return deferred.reject(err);
if(found) {
res.status(200).send({"error_code" : "INVALID_REQUEST_ERROR", "message" : "Username already exists"});
deferred.reject();
} else {
// no user is found, resolve it
deferred.resolve();
}
});
} else {
deferred.reject();
}
return deferred.promise;
}
Q.all([findUserByName(), findUserByEmail()]).then (function() {
// in the resolve function, namely, no exist username and email
user.save(function(err){
if(err) return next(err);
res.status(200).send(user);
});
});

Nodejs undefined variable

I have an empty database and im getting TypeError: Cannot read property 'id' of undefined
Im not sure how to check for a undefined variable, or even if this check should be in the db model
Express route
app.all("/", function(req, res){
if(!req.isAuthenticated()) req.user = null;
Bid.findHighestBids(function(err, bids){
if(err) throw err;
User.findHighestBidder(bids[0].id, bids[0].amount, function(err, highest){
if(err) throw err;
highest.amount = bids[0].amount;
res.render("home", {user: req.user, bids: req.bids, highest: highest});
});
});
});
Snippet from the models, (there is no data so its not returning anything, which is the problem)
BidSchema.statics.findHighestBids = function(done){
var Bids = this;
var num = 5;
this.find()
.sort('-amount')
.limit(num)
.exec(function(err,bids){
if(err) throw err;
done(null, bids);
});
}
UserSchema.statics.findHighestBidder = function(id, amount, done){
var User = this;
this.findOne({ 'facebook.id' : id }, function(err, highest){
if(err) throw err;
if(!id) return done(null, highest);
done(null, highest);
});
}
You're not checking that bids contains any elements before accessing the first one. Since you say you have no data, that's likely your problem:
Bid.findHighestBids(function(err, bids){
if(err) throw err;
User.findHighestBidder(bids[0].id, bids[0].amount, function(err, highest){
...
bids[0] returns undefined, which has no id property, and thus the error.
So do something like this instead:
Bid.findHighestBids(function(err, bids){
if (err) throw err;
if (bids.length) {
User.findHighestBidder(bids[0].id, bids[0].amount, function(err, highest){
if(err) throw err;
highest.amount = bids[0].amount;
res.render("home", {user: req.user, bids: req.bids, highest: highest});
});
} else {
res.render(... whatever you need for the no bids case ...);
}

Resources