When i run my below code i get the error that await is only supported in an async function. How can i make the below async as my route is async but i guess because i am calling a promise within my async function i need make that async to. Below is the route
contactRoutes.get('/:id', async(req, res) => {
cb.getDoc(req.bucket, req.params.id ).then(result=>{
var tasks = await cb.n1qlQuery_wId(req.bucket,req.N1qlQuery, cbQ.qContactTasks,req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true , Error: "", Message:"", Data: result.value})
}).catch(err=>{
res.json({ Success: false , Error: err, Message: ""})
})
})
Try this:
contactRoutes.get('/:id', async(req, res) => {
try{
let result = await cb.getDoc(req.bucket, req.params.id );
let tasks = await cb.n1qlQuery_wId(req.bucket,req.N1qlQuery, cbQ.qContactTasks,req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true , Error: "", Message:"", Data: result.value})
}
catch(err){
res.json({ Success: false , Error: err, Message: ""})
}
})
You have to pass a aync callback into your promise resolver then like this,
contactRoutes.get('/:id', async(req, res) => {
cb.getDoc(req.bucket, req.params.id ).then(async (result)=>{
...........
}
})
or you may also use await in your promise call like this,
contactRoutes.get('/:id', async (req, res) => {
var result = await cb.getDoc(req.bucket, req.params.id)
var tasks = await cb.n1qlQuery_wId(req.bucket, req.N1qlQuery, cbQ.qContactTasks, req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true, Error: "", Message: "", Data: result.value })
}).catch(err => {
res.json({ Success: false, Error: err, Message: "" })
});
Make the callback function in .then async. See code below.
contactRoutes.get('/:id', async(req, res) => {
cb.getDoc(req.bucket, req.params.id ).then(async result=>{
var tasks = await cb.n1qlQuery_wId(req.bucket,req.N1qlQuery, cbQ.qContactTasks,req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true , Error: "", Message:"", Data: result.value})
}).catch(err=>{
res.json({ Success: false , Error: err, Message: ""})
})
})
you have add async to then(...)'s handler AND not the route. Why? Since, await is used in that handler.
You can do this:
contactRoutes.get('/:id', async(req, res) => {
try {
const result = await cb.getDoc(req.bucket, req.params.id )
var tasks = await cb.n1qlQuery_wId(req.bucket,req.N1qlQuery, cbQ.qContactTasks,req.params.id)
console.log(JSON.stringify(tasks))
res.json({ Success: true , Error: "", Message:"", Data: result.value})
} catch (err) {
res.json({ Success: false , Error: err, Message: ""})
}
})
Related
I have a fairly bare bones mern stack and im trying to call getUsers and then retrieve a single user from the returned list of users.
however using [] doesnt seem to work. It looks like getUsers correctly returns the list of users but idk how to pull a single one out
user-ctrl.js
const User = require('../models/user-model')
createUser = (req, res) => {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'You must provide a user',
})
}
const user = new User(body)
if (!user) {
return res.status(400).json({ success: false, error: err })
}
user
.save()
.then(() => {
return res.status(201).json({
success: true,
id: user._id,
message: 'User created!',
})
})
.catch(error => {
return res.status(400).json({
error,
message: 'User not created!',
})
})
}
updateUser = async (req, res) => {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'You must provide a body to update',
})
}
User.findOne({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(404).json({
err,
message: 'User not found!',
})
}
user.name = body.name
user.email = body.email
user
.save()
.then(() => {
return res.status(200).json({
success: true,
id: user._id,
message: 'User updated!',
})
})
.catch(error => {
return res.status(404).json({
error,
message: 'User not updated!',
})
})
})
}
deleteUser = async (req, res) => {
await User.findOneAndDelete({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!user) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: user })
}).catch(err => console.log(err))
}
getUserById = async (req, res) => {
await User.findOne({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!user) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: user })
}).catch(err => console.log(err))
}
getUsers = async (req, res) => {
await User.find({}, (err, users) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!users.length) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: users })
}).catch(err => console.log(err))
}
module.exports = {
createUser,
updateUser,
deleteUser,
getUsers,
getUserById,
}
You need to actually call the getUsers function (with parenthesis), and then wait for the promise to resolve, with await
var allUsers = await UserCtrl.getUsers();
var defaultUser = allUsers[0];
or
UserCtl.getUsers()
.then(u=>u[0])
.then(user=>{
// insert code that uses the user here
})
It's a promise, so try with async/await
var allUsers = await UserCtrl.getUsers();
var defaultUser = allUsers[0];
To make await work, put async infront of your method:
async createUser = (req, res) => {
I'm using express and nodejs and have trouble with and then method logging a promise that stays pending.
edit().then(data => console.log(data));
Here is the edit function.
async function edit(data, id) {
let response = await fetch(config_url+'/subscriber/' + id, {
headers : { "content-type" : "application/json; charset=UTF-8"},
method: 'PUT',
body: JSON.stringify(data)
});
let ret = await repsonse.json();
return ret;
}
The rest api is express js.
Subscriber.edit = function (name, email, id, result) {
sql.query("UPDATE subscribers SET name=?, email=? WHERE id = ?", [name, email, id], function (err, res) {
if (err) {
console.log("error: ", err);
result(null, err);
} else {
console.log(res);
result(res);
}
});
};
The data changes in the database but below the res.send() line "subscriber changed successfully" in postman.
exports.edit_subscriber = function (req, res) {
console.log(req.params);
console.log(req);
console.log(res);
Subscriber.edit(req.body.name, req.body.email, req.params.id, function(err, subscriber) {
if (err) {
res.sendStatus(err);
}
res.send({ message: 'Subscriber succesfully edited'});
});
};
Again why does my own async function return a Promise that is not resolved and stays pending in the console of chrome.
EXPRESS ERROR
'access-control-allow-origin': [ 'Access-Control-Allow-Origin', '*' ]
}
}
OkPacket {
fieldCount: 0,
affectedRows: 1,
insertId: 0,
serverStatus: 2,
warningCount: 0,
message: '(Rows matched: 1 Changed: 1 Warnings: 0',
protocol41: true,
changedRows: 1
}
/Users/[classified]/repos/[classified]]/node_modules/mysql/lib/protocol/Parser.js:437
throw err; // Rethrow non-MySQL errors
^
RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: OkPacket {
fieldCount: 0,
affectedRows: 1,
insertId: 0,
serverStatus: 2,
warningCount: 0,
message: '(Rows matched: 1 Changed: 1 Warnings: 0',
protocol41: true,
changedRows: 1
}
at ServerResponse.writeHead (_http_server.js:241:11)
at ServerResponse._implicitHeader (_http_server.js:232:8)
at write_ (_http_outgoing.js:607:9)
at ServerResponse.end (_http_outgoing.js:717:5)
you have to stringify the res.send(JSON.stringify(json))
exports.edit_subscriber = function (req, res) {
...
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ message: 'Subscriber succesfully edited' }));
};
Also in the below code, you are passing the parameters to the callback incorrectly. The first parameter is err, second parameter is result. its not passed correctly.
Subscriber.edit = function (name, email, id, resultCallBack) {
sql.query("UPDATE subscribers SET name=?, email=? WHERE id = ?", [name, email, id], function (err, res) {
if (err) {
console.log("error: ", err);
resultCallBack(err);
} else {
console.log(res);
resultCallBack(null, res);
}
});
};
Also res.sendStatus accepts only status code.
res.sendStatus(400).send(JSON.stringify{message: 'error occured'});
hope this helps
you have typos and uneeded "let"
async function edit(data, id) {
let response = await fetch(config_url+'/subscriber/' + id, {
headers : { "content-type" : "application/json; charset=UTF-8"},
method: 'PUT',
body: JSON.stringify(data)
});
let ret = await repsonse.json();
return ret;
}
repsonse.json() won't work.
No need to let if you don't modify them later. Also, JSON.stringify might fail and you don't have any trycatch block.
async function edit(data, id) {
let serializedData = "";
try {
serializedData = JSON.stringify(data);
catch (error) {
// do something with error
}
const response = await fetch(config_url+'/subscriber/' + id, {
headers : { "content-type" : "application/json; charset=UTF-8"},
method: 'PUT',
body: serializedData
});
return response.json();
}
I have a function that look like this, for now at least it's working.
exports.changePassword = (req, res) => {
const { token, password, confirmPassword } = req.body
User.findOne({resetPasswordToken: token}, (err, user)=>{
if(!user){
return res.status(400).send({
msg: 'Invalid token or token has been used!'
})
}
const hash_password = bcrypt.hashSync(password, 10)
User.findOneAndUpdate({_id: user._id},
{hash_password},
(err, result)=>{
if(err){
return res.status(400).send({
msg: err
})
}
User.findOneAndUpdate({_id: user._id},
{resetPasswordToken: ''},
(err, result)=>{
if(err){
return res.status(400).send({
msg: err
})
}
res.status(200).json({
status: 1,
data: 'Your password has been changed.'
})
}
)
})
})
}
I just felt bad writing this block of code, because I think it has several problems:
callback hell
duplication of error handling code
For first problem maybe I can use done argument? and do some chaining? And also sometime I doubt I need to handle every single err callback. How would you rewrite above function to become more elegant?
You can use promises with Mongoose, which will help with your callback hell:
exports.changePassword = (req, res) => {
const { token, password, confirmPassword } = req.body
User.findOne({resetPasswordToken: token}).then((user)=>{
// do stuff with user here
const hash_password = bcrypt.hashSync(password, 10)
// Now chain the next promise by returning it
return User.findOneAndUpdate({_id: user._id}, {hash_password});
}).then((result)=>{
// Now you have the result from the next promise, carry on...
res.status(200).json({
status: 1,
data: 'Your password has been changed.'
})
}).catch(err => {
// Handle any errors caught along the way
});
}
Since these are promises, you can actually make this even neater by using the ES6 async/await syntax:
// Note this now has the async keyword to make it an async function
exports.changePassword = async (req, res) => {
const { token, password, confirmPassword } = req.body
try {
// Here is the await keyword
const user = await User.findOne({resetPasswordToken: token});
// do stuff with user here
const hash_password = bcrypt.hashSync(password, 10)
// Now the next promise can also be awaited
const result = await User.findOneAndUpdate({_id: user._id}, {hash_password});
// Finally send the status
res.status(200).json({
status: 1,
data: 'Your password has been changed.'
});
} catch (err) {
// Any promise rejections along the way will be caught here
});
}
To avoid this ugly Promise hell we have
ES2017 async/await syntax
You should change your whole code for something like this
exports.changePassword = async function (req, res){
try {
const { token, password, confirmPassword } = req.body
var user = await User.findOne({resetPasswordToken: token}).exec()
const hash_password = bcrypt.hashSync(password, 10)
var result = await User.findOneAndUpdate({_id: user._id}, {hash_password}).exec()
var result2 = await User.findOneAndUpdate({_id: user._id}, {resetPasswordToken: ''}).exec()
res.status(200).json({status: 1, data: 'Your password has been changed.'})
} catch (err) {
res.status(400).send({msg: err }) //If some await reject, you catch it here
}
}
I was creating my users API, I want to check if username had been used.
So I wrote a static function
static findByName(name) {
const query = User.where({
username: name,
});
query.findOne((queryErr, user) => {
if (queryErr) {
console.log(queryErr);
return false;
}
return user;
});
}
when I called it in signUp
signup(req, res) {
if (!req.body.username || !req.body.password || !req.body.email) {
return res.status(400).json({ success: false, message: 'Bad Request' });
}
if (!Users.findByName(req.body.username)) {
return res.status(409).json({ success: false, message: 'Username has been used' });
}
const hashedPassword = this.genHash(req.body.password);
const newUser = User({
username: req.body.username,
});
}
findByName return Undefined.
Finally I use promise.
signup(req, res) {
if (!req.body.username || !req.body.password || !req.body.email) {
return res.status(400).json({ success: false, message: 'Bad Request' });
}
return Users.findByName(req.body.username).then((existingUser) => {
if (existingUser) {
return res.status(409).json({ success: false, message: 'Username has been used' });
}
const hashedPassword = this.genHash(req.body.password);
const newUser = User({
username: req.body.username,
password: hashedPassword,
email: req.body.email,
});
return newUser.save().then((user) => {
res.json({ success: true, user });
}).catch((err) => {
res.status(500).json({ success: false, message: 'Internal Server Error' });
});
}).catch((err) => {
res.status(500).json({ success: false, message: 'Internal Server Error' });
});
}
That is really horrible code.
Is there better way to clean the code?
Is there better way to clean the code
Yes. I am going to assume /signup is defined as a POST route on the usual Express app instance
With that said you, since you are already using promises, you can go a step further and use async/await which is enabled by default in Node.js v7.6+.
This will make your code read more synchronously:
async signup(req, res) {
if (!req.body.username || !req.body.password || !req.body.email) {
return res.status(400).json({ success: false, message: 'Bad Request' });
}
try {
const existingUser = await Users.findByName(req.body.username)
if (existingUser) {
return res.status(409).json({ success: false, message: 'Username has been used' })
}
const hashedPassword = this.genHash(req.body.password);
const newUser = await User({
username: req.body.username,
password: hashedPassword,
email: req.body.email,
}).save()
res.json({ success: true, newUser });
} catch (error) {
res.status(500).json({ success: false, message: 'Internal Server Error' });
}
}
You may have noticed the use of try/catch. This is because since we are not using .catch() and we still have to handle any error that occurs. To further clean up to code, we can write an error handler middleware that will take care of errors for us:
src/middleware/error-handlers.js
// Wraps the router handler, catches any errors, and forwards to the next middleware that handles errors
exports.catchErrors = action => (req, res, next) => action(req, res).catch(next);
// Notice the first parameter is `error`, which means it handles errors.
exports.displayErrors = (error, req, res, next) => {
const err = error;
const status = err.status || 500;
delete err.status;
err.message = err.message || 'Something went wrong.';
if (process.env.NODE_ENV === 'production') {
delete err.stack;
} else {
err.stack = err.stack || '';
}
res.status(status).json({
status,
error: {
message: err.message,
},
});
};
Now we just need to use our error handlers:
app.js
const { catchErrors, displayErrors } = require('./middleware/error-handlers')
// Whenever you defined the function, needs to have the `async` keyword
async function signup(req, res) { ... }
// Wrap the function call
app.post('/signup', catchErrors(signUp))
// Handle any errors
app.use(displayErrors)
Using the above middleware transforms our code to:
async signup(req, res) {
const error = new Error()
if (!req.body.username || !req.body.password || !req.body.email) {
error.status = 400
error.message = 'Bad Request'
throw error
}
const existingUser = await Users.findByName(req.body.username)
if (existingUser) {
error.status = 409
error.message = 'Username has been used'
throw error
}
const hashedPassword = this.genHash(req.body.password);
const newUser = await User({
username: req.body.username,
password: hashedPassword,
email: req.body.email,
}).save()
res.json({ success: true, newUser });
}
You can see how the code is much easier to read without all the noise.
Be sure to read up on:
Writing middleware for use in Express apps
Express error handling
I can't test that my solution actually works (probably not) or that I'm covering every functionality of your original code. What I tried to do is to make functions that handle specific things, then chain them together, so that you can see a more clear flow of the program.
signup(req, res) {
if (!req.body.username || !req.body.password || !req.body.email) {
return res.status(400).json({ success: false, message: 'Bad Request' });
}
const handleError = error => res.status(500).json({ success: false, message: 'Internal Server Error' });
const handleExistingUser = existingUser => {
if (existingUser) {
return res.status(409).json({ success: false, message: 'Username has been used' });
} else {
return Promise.resolve();
}
}
const handleCreateUser = () => {
const hashedPassword = this.genHash(req.body.password);
const newUser = User({
username: req.body.username,
password: hashedPassword,
email: req.body.email,
});
return newUser.save().then((user) => {
res.json({ success: true, user });
});
};
// the flow of the program is hopefully more clear here
return Users.findByName(req.body.username)
.then(handleExistingUser)
.then(handleCreateUser)
.catch(handleError);
}
If you handle the error of the inner and outer promise the same, then I think it's enough to have the error handling in the outer layer. (In your last example.) But I'm not 100% certain.
Your function returns undefined, because it does not have a return statement. The return user statement, is the (useless) return value for the findOne callback function, not for findByName.
If you go for promises, then define the function as follows:
static findByName(name) {
return User.where({ username: name }).findOne().exec();
}
And your promise chain can be simplified a bit, like this:
signup(req, res) {
function report(message, status = 200) {
res.status(status).json({ success: status === 200, message });
}
if (!req.body.username || !req.body.password || !req.body.email) {
return report('Bad Request', 400);
}
Users.findByName(req.body.username).then((existingUser) => {
return existingUser ? null // treat this condition in the next `then`
: User({
username: req.body.username,
password: this.genHash(req.body.password),
email: req.body.email,
}).save().exec();
}).then((user) => {
return existingUser ? report(user)
: report('Username has been used', 409);
}).catch((err) => {
report('Internal Server Error', 500);
});
}
I have an async function
async function getPostAsync() {
const post = await Post.findById('id');
// if promise was successful,
// but post with specific id doesn't exist
if (!post) {
throw new Error('Post was not found');
}
return post;
}
I am calling the function with
app.get('/', (req, res) => {
getPostAsync().then(post => {
res.json({
status: 'success',
});
}).catch(err => {
res.status(400).json({
status: 'error',
err
});
})
});
but I just receive
{
"status": "error",
"err": {}
}
I would expect to either get the error Post was not found or some error with the connection or something like that, but the variable err is simply an empty object in my catch statement.
Consider the following:
let e = Error('foobar');
console.log( JSON.stringify(e) )
This outputs {}, much like in your case. That's because errors don't serialize to JSON very well.
Instead, try this:
res.status(400).json({
status : 'error',
err : err.message // `String(err)` would also work
});