Node application crash if i use send or json - node.js

I am creating a IOS Application and in background we have used Node.js and Mongodb. I have create node application for creating user and send error and success response by jSon but if use res.send my node application will crash.
I have tried to found issue but not get yet and positive response. Below my all code.
Controller :
const nodemailer = require('nodemailer');
const passport = require('passport');
const User = require('../../models/User');
exports.ManuallySave = function(req,res)
{
if(req.body.my_token !== '')
{
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (existingUser)
{
//res.setHeader('Content-Type', 'text/plain');
res.json({"status":'error',"msg":'Email address already exists.'});
}
});
User.findOne({ username: req.body.username }, (err, existingUserName) => {
if (existingUserName)
{
res.send({"status":'error',"msg":'Username already exists.'});
}
});
/* Save Action perform */
}
else
{
res.send({"status":'error',"msg":'Token is not available.'});
}
}
Console Error.
/var/www/node/MyApplication/node_modules/mongodb/lib/utils.js:98
process.nextTick(function() { throw err; });
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
at ServerResponse.header (/var/www/node/MyApplication/node_modules/express/lib/response.js:718:10)
at ServerResponse.send (/var/www/node/MyApplication/node_modules/express/lib/response.js:163:12)
at ServerResponse.json (/var/www/node/MyApplication/node_modules/express/lib/response.js:249:15)
at ServerResponse.send (/var/www/node/MyApplication/node_modules/express/lib/response.js:151:21)
at /var/www/node/MyApplication/controllers/apis/userAppController.js:55:13
at model.<anonymous> (/var/www/node/MyApplication/node_modules/mongoose/lib/document.js:1875:20)
at next_ (/var/www/node/MyApplication/node_modules/hooks-fixed/hooks.js:89:34)
at fnWrapper (/var/www/node/MyApplication/node_modules/hooks-fixed/hooks.js:186:18)
at /var/www/node/MyApplication/node_modules/mongoose/lib/model.js:226:5
at /var/www/node/MyApplication/node_modules/mongoose/lib/model.js:135:7
at /var/www/node/MyApplication/node_modules/mongodb/lib/collection.js:504:5
at /var/www/node/MyApplication/node_modules/mongodb/lib/collection.js:666:5
at handleCallback (/var/www/node/MyApplication/node_modules/mongodb/lib/utils.js:96:12)
at /var/www/node/MyApplication/node_modules/mongodb/lib/bulk/unordered.js:473:9
at handleCallback (/var/www/node/MyApplication/node_modules/mongodb/lib/utils.js:96:12)
at resultHandler (/var/www/node/MyApplication/node_modules/mongodb/lib/bulk/unordered.js:420:5)
at /var/www/node/MyApplication/node_modules/mongodb-core/lib/wireprotocol/2_4_support.js:544:17
at nextTickCallbackWith0Args (node.js:420:9)
at process._tickCallback (node.js:349:13)
I have used res.send and res.json both but in both condition my application will crash.

That error is thrown when you are trying to call methods on res when you already called res.send. There is no guarantee that it res.send only will be called once in your code, which there must be. The block
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (existingUser)
{
//res.setHeader('Content-Type', 'text/plain');
res.json({"status":'error',"msg":'Email address already exists.'});
}
});
User.findOne({ username: req.body.username }, (err, existingUserName) => {
if (existingUserName)
{
res.send({"status":'error',"msg":'Username already exists.'});
}
});
will call res.send twice if you already have a user with both that email address and that username. You will have to do the other call within the first callback.
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (existingUser)
{
//res.setHeader('Content-Type', 'text/plain');
res.json({"status":'error',"msg":'Email address already exists.'});
} else {
checkUsername();
}
});
function checkUsername() {
User.findOne({ username: req.body.username }, (err, existingUserName) => {
if (existingUserName)
{
res.send({"status":'error',"msg":'Username already exists.'});
} else {
// save action
}
});
}
May I suggest you look into promise chains to handle the inevitable upcoming callback nesting?

You should just use the or operator.
User.findOne({ $or: [ { email: req.body.email }, { username: req.body.username } ] }, (err, existingUser) => {
if (existingUser)
{
//res.setHeader('Content-Type', 'text/plain');
res.json({"status":'error',"msg":'Email address already exists.'});
}
});
Than you just send one response.

You're getting this error because your code allows for a res.send() after a res.json() has already been issues.
Your two validation checks both execute asynchronously, and if both validation conditions are met, then both res.json and res.send will be executed.
One solution is to embed the second check within the callback of the first like this:
const nodemailer = require('nodemailer');
const passport = require('passport');
const User = require('../../models/User');
exports.ManuallySave = function(req,res){
if(req.body.my_token !== ''){
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (existingUser) {
res.json({"status":'error',"msg":'Email address already exists.'});
} else {
User.findOne({ username: req.body.username }, (err, existingUserName) => {
if (existingUserName) {
res.send({"status":'error',"msg":'Username already exists.'});
} else {
/* Save Action perform */
}
});
}
});
} else {
res.send({"status":'error',"msg":'Token is not available.'});
}
}
I'd also recommend checking err in both of those callbacks and handling them.

Related

reset password token nodejs and express, throw er; // Unhandled 'error' event ^ Error: Callback was already called

I am trying to reset password using nodemailer. I am sending a mail to the user mail address with a link to reset password. After clicking on update password link on the page it is throwing the following error:-
throw er; // Unhandled 'error' event
^
Error: Callback was already called.
I can't get what's the error and what is meant by callback is already called?
here is my post request code :-
app.post('/reset/:token', function(req, res) {
async.waterfall([
function(done) {
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('error', 'Password reset token is invalid or has expired.');
return res.redirect('back');
}
user.password = req.body.password;
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.save(function(err) {
req.logIn(user, function(err) {
done(err, user);
});
});
});
// res.redirect('/login');
},
function(user, done) {
var smtpTransport = nodemailer.createTransport('SMTP', {
// host:'smtp.gmail.com',
service: 'Gmail',
auth: {
user: 'myemail',
pass: 'password'
}
});
var mailOptions = {
to: user.email,
from: 'my email',
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
done(err);
});
}
], function(err) {
res.redirect('/');
});
});
I am not getting any confirmation mail too on the user email..Thanks in advance
There are several similar mistakes in your code that can cause the error Error: Callback was already called.
When you do a ( for example ) return res.redirect('back'); within a callback, the return statement does not affect the parent function. So, it can happen that res.something has already happened before res.anotherSomething is called, and that is not allowed.
Instead of using callbacks, try using async-await.
ok, I got the mistake, removing SMTP from nodemailer.createTransport solved the issue. However I am still not getting the flash-message.

How to pull status response codes from the backend and use it in the frontend?

I am trying to send status response codes from the backend which is in Node.js to my frontend which is in Angular in order to display an error message "Email already exists".
// Frontend
add.user.component.ts
if (this.AddUserForm.valid) {
this.AuthService.register(this.AddUserForm.value)
.pipe(first())
.subscribe(
data => {
this.router.navigate(['dashboard/user']);
this.snackbar.open('User has been Added', 'Close', {
duration: 3000,
});
},
error => {
this.loading = false;
this.snackbar.open('Unsuccessful', 'Close', {
duration: 3000,
});
},
);
}
authentication.service.ts
register(user: User) {
// const headers = new HttpHeaders({ 'Authorization': JSON.parse(localStorage.getItem('currentUser')).token });
return this.http.post<any>(`${this.environment.userUrl}/auth/register`, user);
}
// Backend
authentication.js
exports.register = function (req, res, next) {
var email = req.body.email;
var password = generator.generate({
length: 10,
numbers:true,
uppercase:true,
symbols: true
});
if (!email) {
return res.status(422).send({
error: 'You must enter an email address'
});
}
User.findOne({
email: email
}, function (err, existingUser) {
if (err) {
return next(err);
}
if (existingUser) {
return res.status(422).send({
error: 'That email address is already in use'
});
}
var user = new User({
email: email,
password: password,
});
user.save(function (err, user, done) {
if (err) {
return next(err);
}
var userInfo = setUserInfo(user);
res.status(201).json({
message: 'Please Check you Email Address',
token: 'JWT ' + generateToken(userInfo),
user: userInfo
});
The response code needs to be sent from the backend to my frontend. Here, in this case, response status code 422 needs to be sent to my frontend
In the response object you can add the statuCode value.
if (existingUser) {
return res.status(422).send({
error: 'That email address is already in use',
statusCode: 422
});
}
You should be able to just read the status directly on the HttpResponse without including it in the response json:
this.AuthService.register(this.AddUserForm.value)
.pipe(first())
.subscribe(data => console.log(data.status));
You might have to add this too to the http call:
{observe: 'response'}
So it becomes like this:
this.http.post<any>(`${this.environment.userUrl}/auth/register`, user, {observe: 'response'});

NodeJS : Hapi + Mongoose | return on a callback not working

I'm using hapi and mongoose for an easy login system.
server.route({
method: 'POST',
path: '/register',
config: {
handler: (request, h) => {
const username = request.payload.username.toLowerCase();
const email = request.payload.email.toLowerCase();
const password = request.payload.password;
const passwordconfirm = request.payload.passwordconfirm;
if(password==passwordconfirm && username && email && password) {
var userData = {
email: email,
username: username,
password: password,
verified: false
}
User.create(userData, function (err, user) {
//if there is an error about email
if (err.toString().indexOf("email")>-1) {
return statusGenerator(false, "Email already in use", null);
}
//if there is an error about email
if (err.toString().indexOf("username")>-1) {
return statusGenerator(false, "Username already in use", null);
}
//send email to specified email
return statusGenerator(true,"Confirm your email",null);
});
}
}
}
});
statusGenerator() generate a JSON which should be sent back as response.
In hapi i can return from the handler to make this happen but I have no idea how to get that value from the callback and send as response.
Here's the error.
Debug: internal, implementation, error
Error: handler method did not return a value, a promise, or throw an error
at module.exports.internals.Manager.execute (c:\Users\me\path\node_modules\hapi\lib\toolkit.js:52:29)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
I solved it using a Promise:
return new Promise((resolve) => {
User.create(userData, function (err, user) {
if(err)
{
if (err.toString().indexOf("email")>-1) {
resolve(statusGenerator(false, "Email already in use", null));
}
if (err.toString().indexOf("username")>-1) {
resolve(statusGenerator(false, "Username already in use", null));
}
}
//send email to specified email
resolve(statusGenerator(true,"Confirm your email",null));
});
});

How to Async Mongoose Controller

Trying to configure a SignUp() controller that can update multiple (separate) user accounts when a referral code is provided by the user.
Basic Flow:
Verify email doesn't already exist in system
Find the driver w/ userID matching the rider's refCode (FindOneAndUpdate)
If Found: Add the userID of each user to the other users [clients] list
Only need to do a refCode match if isRider
If any of those fail... Return the specific error to the client/user
This does not work. But essentially, this is what I'm trying to accomplish...
// POST `/signup` (Create a new local user)
export function signUp(req, res, next) {
const newUser = new User({
email: req.body.email,
password: req.body.password,
profile: {
userID: req.body.userID,
refCode: req.body.refCode,
isRider: req.body.isRider
}
});
User.findOne({ email: req.body.email }, (findErr, foundUser) => {
if (foundUser) {
return res.status(409).send('This e-mail address already exists');
}
// riders must link to a driver
if (req.body.isRider) {
// find driver + add rider ID to clients
return User.findOneAndUpdate({ 'profile.userID': req.body.refCode }, { $push: { clients: newUser.profile.userID }}).exec()
.then((err, foundDriver) => {
if (err) {
return res.status(409).send('Error searching for driver');
} else if (!foundDriver) {
return res.status(409).send(`We can't find your driver (${req.body.refCode})!`);
}
// add driver ID to rider's clients
newUser.clients = [req.body.refCode];
return newUser.save((saveErr) => {
if (saveErr) return next(saveErr);
return req.logIn(newUser, (loginErr) => {
if (loginErr) return res.sendStatus(401);
return res.json(newUser.profile);
});
});
});
}
return newUser.save((saveErr) => {
if (saveErr) return next(saveErr);
return req.logIn(newUser, (loginErr) => {
if (loginErr) return res.sendStatus(401);
return res.json(newUser.profile);
});
});
});
}
Tried to configure it as a pure promise but no luck. Most of the examples out there all seem different to me... Also could not figure out how to handle/throw specific errors using the mongoose docs.
Greatly appreciated if anyone can lend a hand, Thx!
UPDATE:
Ippi's answer helped a ton - Thx!
This does the trick. Remember to return null from .then() after the req.login stuff to avoid warnings - Any tips on how to improve this are appreciated - Thx!
const createUser = (foundUser) => {
if (foundUser) { throw new Error('This e-mail address already exist.'); }
if (!req.body.isRider) { return newUser.save(); }
return User.findOneAndUpdate({ 'profile.userID': req.body.refCode.toLowerCase() }, { $push: { clients: newUser.profile.userID }}).exec()
.then((driver) => {
if (!driver) { throw new Error('We can\'t find your driver.'); }
newUser.clients = [req.body.refCode];
return newUser.save();
})
.catch(() => { throw new Error('There was a database error.'); });
};
User.findOne({ email: req.body.email }).exec()
.then(createUser)
.then((user) => {
if (user.profile) {
req.logIn(user, (loginErr) => {
if (loginErr) return res.sendStatus(401);
return res.status(200).send({ profile: user.profile, clients: user.clients });
});
} else { res.status(409); }
return null;
})
.catch((err) => { return res.status(409).send(err.message); });
function signUp(req, res, next) {
return new Promise((resolve, reject) => {
const newUser = new User({
email: req.body.email,
password: req.body.password,
profile: {
userID: req.body.userID,
refCode: req.body.refCode,
isRider: req.body.isRider
}
});
User.findOne({ email: req.body.email }, (findErr, foundUser) => {
if (foundUser) {
// return res.status(409).send('This e-mail address already exists');
reject('This e-mail address already exists');
}
// riders must link to a driver
if (req.body.isRider) {
// find driver + add rider ID clients
return User.findOneAndUpdate({ 'profile.userID': req.body.refCode }, { $push: { clients: newUser.profile.userID } }).exec()
.then((err, foundDriver) => {
if (err) {
// return res.status(409).send('Error searching for driver');
reject('Error searching for driver');
} else if (!foundDriver) {
// return res.status(409).send(`We can't find your driver (${req.body.refCode})!`);
reject(`We can't find your driver (${req.body.refCode})!`);
}
// add driver ID to rider's clients
newUser.clients = [req.body.refCode];
newUser.save((saveErr) => {
if (saveErr)
// next(saveErr);
reject(saveErr);
req.logIn(newUser, (loginErr) => {
if (loginErr)
// return res.sendStatus(401);
reject('401');
// return res.json(newUser.profile);
resolve(newUser.profile);
});
});
});
}
newUser.save((saveErr) => {
if (saveErr)
// return next(saveErr);
reject(saveErr);
req.logIn(newUser, (loginErr) => {
if (loginErr)
// return res.sendStatus(401);
reject(loginErr);
// return res.json(newUser.profile);
resolve(newUser.profile);
});
});
});
});}
This is how I would do it. I couldn't be bothered to try with express or the login (you need to replace console.log with res.status().json()) and I might have done some other blunder in the logic with the driver. But other than that I tested it with local mongo and it probably works and if nothing else it's a little bit more concise.
let updateUser = user => {
if (user){ throw new Error("USER_EXIST"); }
if (!req.body.isRider) { return newUser.save() }
return User.findOneAndUpdate({ 'profile.userID': req.body.refCode },{ $push: { clients: newUser.profile.userID }}).exec()
.then(driver => {
if (!driver) { throw new Error("NO_DRIVER");}
newUser.clients.push(req.body.refCode);
return newUser.save();
});
}
User.findOne({ email: req.body.email }).exec()
.then(updateUser)
.then(req.logIn) // newUser.save() response is passed in as is (I have not tested this line.)
.then( ()=>{ return console.log('profile', newUser.profile); })
.catch( Error, err => {
if (err.message == "USER_EXISTS") return console.log ("This e-mail address already exist." );
if (err.message == "NO_DRIVER") return console.log ("We can't find your driver." );
throw err;
});
Something worth remembering:
Callback calls or res.send should always go in the last then / catch. Calling res.send in middle of chains leads to trouble.

socket.emit not firing inside post request

I wrapped io.on('connection', function (socket) { in a post request. I then call socket.emit('news', 'username taken'); within the post request. For some reason when I make this call it sends nothing to the client. When I change the emit to io.emit('connection', function (socket)) It works and sends the data to the client. My problem with that solution is using io.emit would send the data to all the sockets that are connected. My question is how can I use socket.emit within this post request.
io.on('connection', onConnection);
function onConnection(sock) {
sock.emit('news', 'username is taken');
}
app.post('/signup', function(req, res) {
var userDetails = User({
firstname: req.body.firstname,
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password1, bcrypt.genSaltSync(10))
});
User.findOne({
$or: [ { 'username': req.body.username}, {'email': req.body.email}]
}, function(err, user) {
if (user) {
if(user.username === req.body.username){
onConnection();
console.log('username is taken');
} else {
}
if(user.email === req.body.email){
console.log('email is taken')
} else {
}
} else {
userDetails.save(function(err) {
if (err) throw err;
});
res.redirect('/');
console.log('change to login')
}
if (err) {
return done(err);
}
});
});
});
The usual way you would approach this is you would use session middleware that would establish a user session when the first page on the site was loaded. Then, when the user connects with socket.io, you would put the socket.id into the session for that user. Then, when you get the app.post() and you want to send to their socket.io connection, you would look in the session for that user, get the socket.id for that user and then look up the socket using that. Once you have the socket, you can send a message to them.
You use the session to connect the socket.io connection and the app.post(). no event handlers are set inside another event handler.
I just fixed my problem. What I did was create a variable called socket1. Then I assigned the socket parameter to socket1 within the io.on annonymous function. I then have socket as a universal variable that I can call wherever I want in my code. I'm not sure if this is programatically correct but it works.
var SOCKET_LIST = {};
var socket1;
io.on('connection', function (socket) {
SOCKET_LIST[socket.id] = socket;
socket1 = socket;
socket.emit('news', 'username taken');
});
app.post('/signup', function(req, res) {
var userDetails = User({
firstname: req.body.firstname,
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password1, bcrypt.genSaltSync(10))
});
User.findOne({
$or: [ { 'username': req.body.username}, {'email': req.body.email}]
}, function(err, user) {
if (user) {
if(user.username === req.body.username){
socket1.emit('news', 'username taken');
console.log('username is taken');
} else {
}
if(user.email === req.body.email){
io.emit('news', 'email taken');
console.log('email is taken')
} else {
}
} else {
userDetails.save(function(err) {
if (err) throw err;
});
res.redirect('/');
console.log('change to login')
}
if (err) {
return done(err);
}
});
});

Resources