Mongoose findByIdAndUpdate successfully updates document but ruturns error - javascript

findByIdAndUpdate() successfully updates document, but returns error which i don't understand.
Here is schema:
const userSchema = mongoose.Schema({
phone: String,
password: String,
token: String
});
const User = mongoose.model('User', userSchema);
And here is function to update user in database
export const login = (req, res) => {
User.findOne({ phone: req.body.phone }, (err, result) => {
if (err) res.status(500).send(`User with ${req.body.phone} doesn't exist. \n Error: ${err}`);
if( result.password === req.body.password ){
// here Console.log(result) returns:
//{
// _id: 5aa28eb4f4a8de28c24e6990,
// phone: '+79781231233434',
// password: 'passsss',
// token: '1520613346284',
// __v: 0
//}
User.findByIdAndUpdate( result.id, { "token": Date.now() },
(err, result) => {
// It gives error, of which stacktrace i give below. But if check database -
// everything is fine, token was updated successfully
if (err) return res.status(500).send('Unable to create token. Error: ', err);
return res.status(200).send(result._id, result.token);
})
} else return res.status(500).send('Incorrect password');
})
}
Here is what i get in console when i do post request with data which should successfully pass this check and get token.
express deprecated res.send(status, body): Use res.status(status).send(body) instead controllers/user.js:17:28
express deprecated res.send(status, body): Use res.status(status).send(body) instead controllers/user.js:16:37
/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/utils.js:423
throw err;
^
RangeError: Invalid status code: Unable to create token. Error:
at ServerResponse.writeHead (_http_server.js:190:11)
at ServerResponse._implicitHeader (_http_server.js:181:8)
at write_ (_http_outgoing.js:635:9)
at ServerResponse.end (_http_outgoing.js:754:5)
at ServerResponse.send (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:221:10)
at ServerResponse.json (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/express/lib/response.js:158:21)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/controllers/user.js:10:38
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/model.js:3930:16
at _init (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2000:14)
at completeOne (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:1995:5)
at cb (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2365:14)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/query.js:2465:14
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongoose/lib/utils.js:418:16
at result (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:413:17)
at session.endSession (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:400:11)
at ClientSession.endSession (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb-core/lib/sessions.js:69:41)
at executeCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:396:17)
at handleCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:128:55)
at /Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/collection.js:2302:12
at result (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:413:17)
at executeCallback (/Users/dmitryklymenko/Documents/projects/project_exchange/server/node_modules/mongodb/lib/utils.js:405:9)
[nodemon] app crashed - waiting for file changes before starting...
I don't understand this error. Why it appears? If update done, why there is an error at all? And why i see warning about deprecations if i use res.status(status).send(body), as it suggests, everywhere? Maybe it's Babel makes problems to me? Anybody know what i need to check to figure out whats going on?

The body parameter can be a Buffer object, a String, an object, or an Array.
For it to work properly, use string interpolation:
User.findByIdAndUpdate( result.id, { "token": Date.now() },
(err, result) => {
// It gives error, of which stacktrace i give below. But if check database -
// everything is fine, token was updated successfully
if (err) return res.status(500).send(`Unable to create token. Error: ${err}`);
return res.status(200).send(`${result._id}, ${result.token}`);
})
Source: node-express error : express deprecated res.send(status): Use res.sendStatus(status) instead

Related

After sending a POST request Error: Cannot set headers after they are sent to the client

I'am unable to find what goes worng with my code ...!when I send A post request get an Error: "Cannot set headers after they are sent to the client"
my model is - where I created comment_schema whrer I have user,content,blog
const mongoose = require("mongoose");
const { ObjectId } = mongoose.Schema;
const comment_schema = new mongoose.Schema(
{
user: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
blog: {
type: mongoose.Schema.Types.ObjectId,
ref: "Blog",
},
},
{ timestamps: true }
);
const Comment = mongoose.model("Comment", comment_schema);
module.exports = Comment;
Controller is - Here is my postComment controller ,firstly I Find A Blog using req.params.blogId ,then I create a comment and save it ..! and last Associate Post with comment
exports.postComment = async (req, res) => {
/// Find A Blog
const blog = await Blog.findOne({ _id: req.params.blogId });
// console.log(blog);
// console.log(req.params.blogId);
/// Create A Comment
const comment = new Comment();
comment.content = req.body.content;
comment.user = req.body.user;
// console.log(req.body.user);
comment.blog = blog._id;
comment.save((err, Blog_comment) => {
if (err) {
return res.status(400).json({
error: "something went wrong please try again ..!",
});
}
res.json({ Blog_comment });
});
/// Associate Post with comment
blog.comment.push(comment._id);
blog.save((err, comment) => {
if (err) {
res.status(400).json({
error: "Saving Comment in DB failed",
});
}
// res.status(200).json(comment);
res.send(comment);
});
// res.send(comment);
};
And router is - this is my POST router ()
router.post("/blog/:blogId/postComment", postComment);
Full Error - I don't understand why I am getting this error
events.js:292
throw er; // Unhandled 'error' event
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to
the client
at ServerResponse.setHeader (_http_outgoing.js:518:11)
at ServerResponse.header (E:\git\backend\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (E:\git\backend\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (E:\git\backend\node_modules\express\lib\response.js:267:15)
at ServerResponse.send (E:\git\backend\node_modules\express\lib\response.js:158:21)
at E:\git\backend\controllers\blog.js:196:9
at E:\git\backend\node_modules\mongoose\lib\model.js:4842:16
at E:\git\backend\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
at E:\git\backend\node_modules\mongoose\lib\model.js:4865:21
at model.<anonymous> (E:\git\backend\node_modules\mongoose\lib\model.js:502:7)
at E:\git\backend\node_modules\kareem\index.js:315:21
at next (E:\git\backend\node_modules\kareem\index.js:209:27)
at E:\git\backend\node_modules\kareem\index.js:182:9
at E:\git\backend\node_modules\kareem\index.js:507:38
at processTicksAndRejections (internal/process/task_queues.js:79:11)
Emitted 'error' event on Function instance at:
at E:\git\backend\node_modules\mongoose\lib\model.js:4844:13
at E:\git\backend\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
[... lines matching original stack trace ...]
at processTicksAndRejections (internal/process/task_queues.js:79:11)
{
code: 'ERR_HTTP_HEADERS_SENT'
}
[nodemon] app crashed - waiting for file changes before starting...
Is there anyone who can help...! please help I am unable to solve this issue ...! please help
Below an solution for your script, the response is sent to the browser only when everything is executed or when the first error is thrown:
exports.postComment = async (req, res) => {
try{
/// Find A Blog
let blog = await Blog.findOne({ _id: req.params.blogId });
/// Create A Comment
let comment = new Comment();
comment.content = req.body.content;
comment.user = req.body.user;
comment.blog = blog._id;
comment = comment.save();
/// Associate Post with comment
blog.comment.push(comment._id);
blog = await blog.save();
res.send(blog );
}catch(err){
res.status(400).json({
error: "Saving Comment in DB failed",
});
}
};

I keep getting a 401 error when trying to login to my app

I have a user/login route set up that fetches a user, compares the users password with a hashed password, generates a token, and then sends the token to the front end, but for some reason I keep getting a 401 error in the console on the front end and it ends up in my last catch block.
I've tried console logging things out, it gets to "hello" but doesn't get into the first .then() which is confusing. Also it seems to sometimes work locally but on my heroku server it never works.
app.post("/user/login", (req, res, next) => {
console.log('hello')
let fetchedUser;
User.find({ email: req.body.email }).then(user => {
if (!user) {
return res.status(401).json({
message: "Auth failed1"
});
}
fetchedUser = user[0];
console.log('req.body.password', req.body.password)
console.log('user password', user.password)
return bcrypt.compare(req.body.password, user[0].password);
}).then(result => {
if (!result) {
return res.status(401).json({
message: "Auth failed2"
});
}
const token = jwt.sign(
{ email: fetchedUser.email, userId: fetchedUser._id },
process.env.JWT_SECRET,
{ expiresIn: "1h" }
);
console.log(token);
res.status(200).json({
token: token,
expiresIn: 3600
});
})
.catch(err => {
return res.status(401).json({
message: "AUTH FAILED 3"
});
});
});
I expect the user to be logged in after using this path, but instead it's throwing an error in the last catch block. It won't even get into the console.log('hello') on my heroku server.
To figure out what's actually causing it to go to the .catch(), you need to find a way to log the actual err that the .catch() gets. Usually, that will tell you what caused the promise rejection or at least tell you where to look further.
If you are having trouble logging it on the server, you can also add more info to the response message you send back to the client and get the info there.
FYI, you also have another issue. When you do return res.status(401).json({ message: "Auth failed1"}); inside a .then(), that's going to send the interpreter onto the next .then() handler which is not what you want.
You can simplify your code by doing throw someError and sending all the 401 responses to your .catch() where you have a single place to send the 401 response. This will cause it to skip the other .then() handlers which is what you want.

How to act on an Express response object based on the result of an asynchronous function?

I am attempting to create a simple component of an Express app that takes login credentials via uri parameters of a GET request and either serves a user landing page or an error page based on whether the provided credentials match the MySQL credentials.
As I understand it, I need to provide the rendering function to credentialsAreValid() as a callback, provide the hash comparison as a callback from the MySQL query... unfortunately I'm strugging with the internals of credentialsAreValid(). As it stands, I get "TypeError: Cannot read property 'res' of undefined", which I think is because credentialsAreValid() resolves before readOnlyConnection.query() calls the callback?
Because readOnlyConnection.query() is a function of MySQL, I have no idea how to pass in it the res parameter from credentialsAreValid(). I'm about two weeks into JS/Express, so it's possible I'm approaching the whole task incorrectly.
Bonus points if anyone can tell me why the match returns false despite casting both to String and appearing to be identical. (MySQL hash is of type BLOB, but it seems to cast okay, and it fails even using the == operator)
Relevant code:
router.get('/login/:username/:hash', function(req, res, next) {
console.log(`attempted login with username=${req.params.username} and hash=${req.params.hash}`);
login.credentialsAreValid(req.params.username, req.params.hash, res, function(res, valid) {
if (valid) {
console.log("Login Successful").
res.render('login', { title: 'success' });
}
else {
console.log("Login Unsuccessful").
res.render('login', { title: 'fail' });
}
})
});
credentialsAreValid = function (username, password, res, callback) {
console.log(`Checking password for user "${username}"`);
var clientHash = String(createHash(password));
console.log(`Hashed ${password} to ${clientHash}`);
var hashesMatch = readOnlyConnection.query(`SELECT passwordHash FROM users WHERE users.username = "${username}";`, function(err, rows, fields) {
var serverHash = (rows.length > 0) ? String(rows[0].passwordHash) : "";
console.log(`Comparing clientHash ${clientHash} with serverHash ${serverHash} (${clientHash == serverHash})`);
return callback(res, clientHash === serverHash);
console.log(`This should never display`);
});
};
Server console output:
attempted login with username=groucho and hash=password
Checking password for user "groucho"
Hashed password to password
Comparing clientHash password with serverHash password (false)
Login Unsuccessful
/home/node/alex-practice/node_modules/mysql/lib/protocol/Parser.js:80
throw err; // Rethrow non-MySQL errors
^
TypeError: Cannot read property 'res' of undefined
at /home/node/alex-practice/routes/index.js:43:46
at Query.<anonymous> (/home/node/alex-practice/login-helper.js:33:16)
at Query.<anonymous> (/home/node/alex-practice/node_modules/mysql/lib/Connection.js:502:10)
at Query._callback (/home/node/alex-practice/node_modules/mysql/lib/Connection.js:468:16)
at Query.Sequence.end (/home/node/alex-practice/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
at Query._handleFinalResultPacket (/home/node/alex-practice/node_modules/mysql/lib/protocol/sequences/Query.js:139:8)
at Query.EofPacket (/home/node/alex-practice/node_modules/mysql/lib/protocol/sequences/Query.js:123:8)
at Protocol._parsePacket (/home/node/alex-practice/node_modules/mysql/lib/protocol/Protocol.js:278:23)
at Parser.write (/home/node/alex-practice/node_modules/mysql/lib/protocol/Parser.js:76:12)
at Protocol.write (/home/node/alex-practice/node_modules/mysql/lib/protocol/Protocol.js:38:16)
[nodemon] app crashed - waiting for file changes before starting...

Server shutting down itself using twitter api with nodejs and expressjs

I have an web app with using Twitter API. Main focus is to get the users that are not following back based on given twitter user name. It works fine until this point but when I get an error because of the fact that user name does not exist on Twitter, Server shuts down itself. Using Nodejs, expressjs.
The Error Message :
App is running on port : 3000
[ { code: 34, message: 'Sorry, that page does not exist.' } ]
_http_outgoing.js:494
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:494:11)
at ServerResponse.setHeader (_http_outgoing.js:501:3)
at ServerResponse.header (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/express/lib/response.js:170:12)
at /home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/twitter-api.js:30:21
at Request._callback (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/twitter/lib/twitter.js:215:14)
at Request.self.callback (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/request/request.js:186:22)
at emitTwo (events.js:126:13)
at Request.emit (events.js:214:7)
at Request.<anonymous> (/home/ugurcan/dev/Projects/Twitter-App/twitter-api-app/node_modules/request/request.js:1163:10)
Problematic part of the code is below. The question is : How can I avoid this situation ? Or is it even possible ?
client.get('followers/ids', params, function(error, followers_results, response) {
if (error) {
res.send("error");
console.log(error);
}
let followers = followers_results.ids;
client.get('friends/ids', params, function(error, following_results, response ) {
if (error) {
res.send("error");
console.log(error);
}
let following = following_results.ids;
following.forEach(function(person){
if(followers.indexOf(person) === -1){
one_way_following.push(person);
}
});
// console.log(one_way_following);
one_way_following = one_way_following.slice(0, 100);
one_way_following_string = one_way_following.join();
// console.log("----------------------------------------------------");
// console.log(one_way_following_string);
// console.log("----------------------------------------------------");
client.get('users/lookup', {user_id: one_way_following_string}, function(error, user_results, response, next) {
if (error) {
res.send("error");
console.log(error);
}
user_results.forEach(function(user){
let userObject = {
name : user.name,
screen_name : user.screen_name,
avatar: user.profile_image_url
}
// console.log(user.name);
users.push(userObject);
})
res.render("results.ejs",{users:users});
// console.log(users);
});
});
});
This error is actually happening because, you can't call res.render("results.ejs",{users:users}); or res.send("error"); multiple times, because headers can't be changed once set.
Easiest solution would be to break out of the function once you've caught an error (if (error) { on line 2), or handle it appropriately (render something blank, or render your friendly error view).
For more information, about this, check out the answer to this question, Error: Can't set headers after they are sent to the client
Hope that helps :)

Payload error in jsonwebtoken

I am making a web application using nodejs and angular cli
I'm using JWT to authenticate my login function . But when I process it threw this error
Error: Expected "payload" to be a plain object.
at validate (D:\Mean_Projects\meanauthapp\node_modules\jsonwebtoken\sign.js:34:11)
at validatePayload (D:\Mean_Projects\meanauthapp\node_modules\jsonwebtoken\sign.js:56:10)
at Object.module.exports [as sign] (D:\Mean_Projects\meanauthapp\node_modules\jsonwebtoken\sign.js:108:7)
at User.comparePassword (D:\Mean_Projects\meanauthapp\routes\users.js:86:27)
at bcrypt.compare (D:\Mean_Projects\meanauthapp\models\user.js:53:9)
at D:\Mean_Projects\meanauthapp\node_modules\bcryptjs\dist\bcrypt.js:297:21
at D:\Mean_Projects\meanauthapp\node_modules\bcryptjs\dist\bcrypt.js:1353:21
at Immediate.next [as _onImmediate] (D:\Mean_Projects\meanauthapp\node_modules\bcryptjs\dist\bcrypt.js:1233:21)
at runCallback (timers.js:785:20)
at tryOnImmediate (timers.js:747:5)
at processImmediate [as _immediateCallback] (timers.js:718:5)
Here my passport code
const JwtStrategy= require('passport-jwt').Strategy;
const ExtractJwt=require('passport-jwt').ExtractJwt;
const User= require('../models/user');
const config=require('../config/database');
module.exports=function(passport){
let opts={};
opts.jwtFromRequest=ExtractJwt.fromAuthHeader();
opts.secretOrKey=config.secret;
opts.issuer = 'accounts.examplesoft.com';
opts.audience = 'yoursite.net';
passport.use(new JwtStrategy(opts,(jwt_payload,done)=>{
console.log(jwt_payload);
User.getUserById(jwt_payload._doc._id,(err,user)=>{
if(err){
return done(err,false);
}
if(user){
return done(null,user);
}
else{
return done(null,false);
}
});
}));
}
My code for authenticate and get profile
// Authenticate
router.post('/authenticate', (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
User.getUserByUsername(username, (err, user) => {
if(err) throw err;
if(!user){
return res.json({success: false, msg: 'User not found'});
}
User.comparePassword(password, user.password, (err, isMatch) => {
if(err) throw err;
if(isMatch){
const token = jwt.sign(user, config.secret, {
expiresIn: 604800 // 1 week
});
res.json({
success: true,
token: 'JWT '+token,
user: {
id: user._id,
name: user.name,
username: user.username,
email: user.email
}
});
} else {
return res.json({success: false, msg: 'Wrong password'});
}
});
});
});
// Profile
router.get('/profile', passport.authenticate('jwt', {session:false}), (req, res, next) => {
res.json({user: req.user});
});
It fails at the line
const token = jwt.sign(user, config.secret, {
With error "Expected "payload" to be a plain object"
Your user object is initialized here:
User.getUserByUsername(username, (err, user)
Which I assume is mongoosejs object, which contains many methods and is not "serializable". You could handle this by passing a plain object, by either using .lean() from mongoose or plain toJSON method:
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
I had this problem as well, with a returned user from mongoose, just add toJSON() or toObject() will fix the issue, but what happens if your user is not always coming from mongoose?
You will get a
user.toJson/user.ToObject is not a function
if you try to do this on a plain object.
If your user is coming from different sources and you don't know if it will be a plain object or not, you can solve it like this:
JSON.parse(JSON.stringify(user));
this is clearly mentioned in the migration doc of passport-jwt
that they have removed the ExtractJwt.fromAuthHeader() from version 2 and 3 and also to use the new method ExtractJwt.fromAuthHeaderAsBearerToken() or one of like that in place of old method. for compelte reference visit
From your log there is the issue
User.comparePassword (D:\Mean_Projects\meanauthapp\routes\users.js:86:27) at
so here four thing need to be updated in your code #every Bit
First in package.json file
Change the version to latest by using * or version no like this
by going to project directory and run the command
npm install passport-jwt --save
"dependencies": {
....
"passport-jwt": "^3.0.1"
}
or
write in the file and run the commadn
`npm install`
"dependencies": {
....
"passport-jwt": "*"
}
Second change this line of your code in authenticate method
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
Third in the passport code change the old method
ExtractJwt.fromAuthHeader();
to new one, from the doc reference you need to use this method opts.jwtFromRequest=ExtractJwt.fromAuthHeaderWithScheme('jwt');
and fourth change this
User.getUserById(jwt_payload._id,(err,user)=>{
This solution will work on latest version's
if you still want to use this old method then
only change the version of your passport-jwt in package.json to 1.x.x (x is the nuber here )of your choise of lower version then 2, by moving to project folder and runing the command npm install
the only thing you need to check is data in the payload_jwt,it will be inside the second layer so please check the jwt_payload.
ok you are all set to go you had already handled User.getUserById(jwt_payload._doc._id,(err,user)=>{
It's very simple, if the user comes from database (mongo) then simply do user.toJSON(), if the user comes from any other source then simply do JSON.stringify(user).
if it's not comming from moongose
then use spread operator
const token = jwt.sign({ ...payload }, config.secret, {
expiresIn: 100080
});
Change
const token = jwt.sign(user, config.secret, { expiresIn: 10080 });
To
const token = jwt.sign(user.toJSON(), config.secret, { expiresIn: 10080 });
const token = jwt.sign(user, config.secret, {
expiresIn: 604800 // 1 week
});
convert this to
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
or you need to console.log(jwt_payload); to find your ID comes under _doc or directly with jwt_payload. because this may change with the versions.

Categories