I am trying to write some code and it work but i ran into a problem where bcrypt makes use of callback and i am trying to resolve it with promise. Here is the code:
`UserSchema.statics.findByData = function(username,password) {
var User = this;
User.findOne({username}).then((user) => {
if(!user){
return Promise.reject();
}
return new Promise((resolve,reject) => {
bcrypt.compare(password,user.password,(err,res) => {
if(res){
resolve(user);
}else {
reject();
}
});
});
});
};`
I call this function from here :
User.findByData(body.username,body.password).then((user) => {
res.send(user);
}).catch((e) => {
res.status(400).send();
});
I get an error: `Cannot read property 'then ' of undefined.
Why i am getting this error?
As stated in the first comment you need to return User.findOne which will be promise. So no need to create another promise. whenever you will return, then will be called with the returned value. You can use Promise.reject() and Promise.resolve() to call catch and then respectively as well.
UserSchema.statics.findByData = function(username,password) {
var User = this;
return User.findOne({username}).then((user) => {
if(!user){
Promise.reject('No user found');
}
bcrypt.compare(password,user.password,(err,res) => {
if(res){
Promise.resolve(user);
}else {
Promise.reject(err);
}
});
});
};
Related
I had this route and it worked perfectly
router.get('/api/User/:id',async(req,res)=>{
try {
const{id}=req.params;
let result =await pool1.request()
.input('Iduser', sql.Int, id)
.execute('GetUser')
res.json(result);
}
catch (err) {
res.json({ error: 'Does Not exist' })
}
});
But I want to separate the function and leave the route as clean as possible, try to separate it as follows but I get the following error: TypeError: one is not a function
Route
router.get('/api/User/:id', async(req,res)=>{
try {
res.json((await one(req.params.id))[0]);
} catch (err) {
console.log(err);
res.sendStatus(500);
}
})
Function
const one = async(id)=>{
return new Promise((resolve,reject)=>{
pool.request()
.input('Iduser', sql.Int, id)
.execute('User')((err,results) =>{
if(err){
return reject(err);
}
resolve(results);
});
});
}
What is my mistake, am I calling the function wrong?
to make your code cleaner you can do this :
const getUserById =async(req,res)=>{
try {
const{id}=req.params;
let result =await pool1.request()
.input('Iduser', sql.Int, id)
.execute('GetUser')
res.json(result);
}
catch (err) {
res.json({ error: 'Does Not exist' })
}
}
router.get('/api/User/:id',getUserById);
also to make it cleaner more you can do it like this
export const asyncHandler = (fn) => async (request, response, next) => {
try {
return await fn(request, response, next);
} catch (error) {
return next(error); // or response.json({ error: 'Does Not exist' })
}
};
const getUserById =async(req,res)=>{
const { params: { id } }=req;
const result =await pool1.request()
.input('Iduser', sql.Int, id).execute('GetUser');
return res.json(result);
}
router.get('/api/User/:id',asyncHandler(getUserById));
Thanks mate, I solved why he tells me that it was not a function, I was not calling it well but the way you explain it the route is much cleaner
function
const one = async(id)=>{
return new Promise((resolve,reject)=>{
pool.request()
.input('IdUser', sql.Int, id)
.execute('GetUser',(err,results)=>{
if(err){
return reject(err);
}
resolve(results);
}
)
});
}
route
router.get('api/user/:id', async(req,res)=>{
try {
let result=await m.one(req.params.id);
res.json(result);
} catch (error) {
console.log(error);
res.sendStatus(500);
}
})
i need to know how i can write my request to make multiple delete.
the second thing is how can i put async function on my code.
i want to delete a campus and in the same time dele the builings with the same id campus in the JSON
app.delete('/campuses/:id', (req, res)=> {
const id = req.params.id;
const details = { 'campusid': new ObjectID(id) };
db.db('').collection('buildings').remove(details, (err, result)=> {
if (err) {
res.send({ 'error': 'en error has occured' });
} else {
res.send(result);
}
});
const details2 = { '_id': new ObjectID(id) };
db.db('').collection('campuses').remove(details2, (err, result)=> {
if (err) {
res.send({ 'error': 'en error has occured' });
} else {
res.send(result);
}
}
);
})
You can delete like this.
app.delete('/campuses/:id', async (req, res)=> {
try {
const id = req.params.id;
const details = { 'campusid': new ObjectID(id) };
await db.db('').collection('buildings').remove(details);
const details2 = { '_id': new ObjectID(id) };
await db.db('').collection('campuses').remove();
res.send(result);
} catch(err) {
return res.json({
success: false,
message: 'error'
});
}
})
You could make sequential functions where the first one calls the second one. You could then pass on variables to the seconds function (ie. your campus ID).
It could look something like this:
const Query1 = (res, query) => {
const request = new sql.Request();
request.query(query, (err, result) => {
if (err) {
return res.json({
success: false,
message: 'error'
});
} else if (result.recordset[0]) {
let campusID = result.recordset;
Query2(res, campusID, query = 'SELECT bla bla')
}
})
}
const Query2 = (res, campusID, query) => {
const request = new sql.Request();
request.query(query, (err, result) => {
if (err) {
return res.json({
success: false,
message: 'error'
});
} else {
return res.json({
success: true
});
}
})
}
There are various ways to make async call.
You can use promises.
Async Functions.
Sending response without waiting for other tasks.
If you want to make parallel calls you can use bluebird join function
I like the syntax of async functions better than promises, but I use both depending on the situation.
Here is an example of running functions in order before moving to the next function:
async.waterfall([
function(callback1) {
//Do some work, then callback
if (error) {
callback1(errorGoesHere,null);
} else {
callback1(null,successMessageGoesHere);
}
},
function(callback2) {
//Do some work, then callback
if (error) {
callback2(errorGoesHere,null);
} else {
callback2(null,successMessageGoesHere);
}
}
], function (error, success) {
if (error) {
//show an error
}
//all good return the response, etc.
});
If anything in these functions fail, it automatically call the end function to catch the error.
I have some problem with promise functions, my app has this structure:
- routes
- service
- db
db is a class initialized when the application start and where I created some wrapper function for insert/find/ecc..., service is a layer between the route and db, here I do most of the work. My problem is that with the code below if a user already exist I want to throw an error or reject the promise but when I try to do something like this I get
Cannot read property 'then' of undefined
where is the error?
This is my resource:
router.put('/', (req, res, next) => {
bcrypt.hash(req.body.password, 10)
.then(function (hash) {
req.body.password = hash;
service.addUser(req.body)
.then((user) => {
return res.json(user);
})
.catch((err) => {
return res.json(err);
});
})
.catch((err) => {
return res.json(err);
});
});
This is the service:
getBy(query) {
return this.mongo.find(query);
}
addUser(data) {
if(!data.email) {
return Promise.reject('email_missing');
}
const self = this;
self.getBy({ email: data.email })
.then((user) => {
if(user.length) {
return Promise.reject('user_exist');
}
return self.mongo.insert(data)
})
.catch((err) => {
return Promise.reject(err);
});
}
and this is the db connection:
find(query) {
const self = this;
return new Promise((resolve, reject) => {
self.collection.find(query).toArray((err, res) => {
if (err) {
self.logger.info('Mongo::find error', err);
reject(err);
} else {
self.logger.info('Mongo::find', query);
resolve(res);
}
});
});
}
insert(data) {
const self = this;
return new Promise((resolve, reject) => {
self.collection.insert(data, (err, res) => {
if (err) {
self.logger.info('Mongo::insert error', err);
reject (err)
} else {
self.logger.info('Mongo::insert', res);
resolve(res)
}
});
});
}
many thanks!
The addUser function does not return a Promise. The code should look like this:
addUser(data) {
if (!data.email) {
return Promise.reject('email_missing');
}
const self = this;
return self.getBy({
email: data.email
})
.then((user) => {
if (user.length) {
return Promise.reject('user_exist');
}
return self.mongo.insert(data)
})
.catch((err) => {
return Promise.reject(err);
});
}
The .catch block here does not make sense because it just contains return Promise.reject(err) so you can remove it:
addUser(data) {
if (!data.email) {
return Promise.reject('email_missing');
}
const self = this;
return self.getBy({
email: data.email
})
.then((user) => {
if (user.length) {
return Promise.reject('user_exist');
}
return self.mongo.insert(data)
});
}
In the router you also have to return the Promise in the .then and you can remove one .catch block:
router.put('/', (req, res, next) => {
bcrypt.hash(req.body.password, 10)
.then(function(hash) {
req.body.password = hash;
return service.addUser(req.body) // return the Promise ehre
})
// the then can be move out here, to avoid nesting
.then((user) => {
return res.json(user);
})
// only on catch is required
.catch((err) => {
return res.json(err);
});
});
An additional note, you should always reject with a real error. So it would be better to write, Promise.reject(new Error('user_exist'))
Nesting promises is an anti-pattern.
See item #2 in Promise Patterns & Anti-Patterns
It's considered an anti-pattern because it reduces understandability and overly complicates the call stack making debugging (more of) a nightmare.
So rather than:
bcrypt.hash(req.body.password, 10)
.then(function (hash) {
req.body.password = hash;
service.addUser(req.body) // ANTI-PATTERN
.then((user) => {
return res.json(user); // [1]
})
.catch((err) => {
return res.json(err); // [2]
});
})
.catch((err) => {
return res.json(err);
});
});
Do this instead:
const SALT_ROUNDS = 10
app.get(URL, function(req, res) {
function setHashedPassword() {
// TODO first test for existence of password in req.body
return bcrypt.hash(req.body.password, SALT_ROUNDS)
.then(hash => req.body.password = hash)
}
function addUser() {
return service.addUser(req.body)
}
Promise.all([ setHashedPassword(), addUser() ])
.then((results) => {
const user = results[1]
res.json(user)
})
.catch((err) => {
res.json(err)
})
})
Note that, at [1] and [2] in the OP's code, it makes no sense to return anything since there is no active context to which you can return a value.
I'd also respond with an object like:
res.json({ok:true, user:user})
and
res.json({ok:false, error:err})
So you can check for success or failure in the client.
Yes, I know you might believe that ok is redundant here, but it's good practice to standardize on a single result value so you don't have to first test for the existence of error before you check for the existence of user.
Hi lets say I have code like this:
router.get('/blablabla', function(req, res, next) {
model
.findOne({condition : req.body.condition})
.then(function(data) {
if(data) {
return res.send("data already exists");
}
else {
//DO CREATE THE NEW DATA
return modelInstance.save();
}
})
.then(function(data) {
if(data) res.send("new data successfully saved");
})
.catch(function(err) {
console.log(err);
});
});
In current condition if data already exists, there will be error
**[Error: Can't set headers after they are sent.]
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)**
Why this error raised? based on my knowledge after res.send() is being called then all the later code below it will not be executed right?
I am return data.save() because I want avoid nesting statement.
UPDATE ALTERNATIVE SOLUTION
router.get('/blablabla', function(req, res, next) {
model
.findOne({condition : req.body.condition})
.then(function(data) {
if(data) {
return new Promise(function(resolve, reject) {
return resolve();
});
}
else {
//DO CREATE THE NEW DATA
return modelInstance.save();
}
})
.then(function(data) {
if(data) res.send("new data successfully saved");
else res.send("data already exists");
})
.catch(function(err) {
console.log(err);
});
});
Your second .then() handler will still execute because the promise is not branched, it's chained so the chain keeps going and thus you will be doing two res.send() operations and that's what causes the error you see.
When you do:
return res.send("data already exists");
You are just returning a value from the .then() handler. Since that value is not a rejected promise, the promise stays resolved and the next .then() handler in the chain will execute.
You could do this:
router.get('/blablabla', function (req, res, next) {
model.findOne({condition: req.body.condition}).then(function (data) {
if (data) {
return res.send("data already exists");
} else {
//DO mMODIFY THE DATA AND THEN SAVE THE DATA
return data.save().then(function () {
res.send("new data successfully saved");
});
}
}).catch(function (err) {
console.log(err);
});
});
Or, you could do this (modification of your second try - assumes that data.save() returns a promise that resolves to a value):
router.get('/blablabla', function(req, res, next) {
model
.findOne({condition : req.body.condition})
.then(function(data) {
if (!data) {
//DO mMODIFY THE DATA AND THEN SAVE THE DATA
return data.save();
}
return data;
})
.then(function(data) {
if(data) res.send("new data successfully saved");
else res.send("data already exists");
})
.catch(function(err) {
console.log(err);
});
});
I am using Promise with Express.
router.post('/Registration', function(req, res) {
var Promise = require('promise');
var errorsArr = [];
function username() {
console.log("1");
return new Promise(function(resolve, reject) {
User.findOne({ username: req.body.username }, function(err, user) {
if(err) {
reject(err)
} else {
console.log("2");
errorsArr.push({ msg: "Username already been taken." });
resolve(errorsArr);
}
});
});
}
var username = username();
console.log(errorsArr);
});
When I log errorsArray, it is empty and I don't know why. I am new in node.js. Thanks in advance.
Try the following, and after please read the following document https://www.promisejs.org/ to understand how the promises work.
var Promise = require('promise');
router.post('/Registration',function(req,res,next) {
function username() {
console.log("agyaaa");
return new Promise(function(resolve,reject) {
User.findOne({"username":req.body.username}, function(err,user) {
if (err) {
reject(err)
} else {
console.log("yaha b agyaaa");
var errorsArr = [];
errorsArr.push({"msg":"Username already been taken."});
resolve(errorsArr);
}
});
});
}
username().then(function(data) {
console.log(data);
next();
});
});
You can have other errors also (or things that shouldn't be done that way). I'm only showing you the basic use of a Promise.
router.post('/Registration', function(req, res) {
return User
.findOne({ username: req.body.username })
.then((user) => {
if (user) {
return console.log({ msg:"Username already been taken" });
}
return console.log({ msg: "Username available." });
})
.catch((err)=>{
return console.error(err);
});
});
you can write a clean code like this.
Promise is a global variable available you don't need to require it.