user.save() does not save (does nothing) in mongodb - node.js

I'm trying to save a user in MongoDB, but it doesn't seem to work. I have no idea why. All i can tell you is that, the code is running and going in the .pre save function but it doesn't enter in the save function promise.
Can you tell me why it is not saving in base ?
Front submit function
handleSubmit(user){
console.log(user);
axios.post('http://localhost:3000/signup', user)
.then(data => {
console.log(data);
console.log("OBVIOUSLY I DON'T SEE THIS LOG EITHER");
}
);
}
app.js (node.js file)
app.post('/signup', urlencodedParser, function(req, res){
let user = new User(req.body);
user.save().then(() => {
console.log("I DON'T SEE THIS LOG");
});
res.end();
});
user.js (node.js file)
UserSchema.pre("save", (next) => {
console.log(this);
console.log("I SEE THIS LOG");
next();
});
const User = mongoose.model('user', UserSchema);
module.exports = User;

maybe a stupid question, but isn't your User in app.js undefined ?
maybe console.log the req.body. because i do not think you are making a user to begin with.
another tip :
use mongoose's create function instead, you will find a example under construction documents:
https://mongoosejs.com/docs/models.html

instead of .then() on your app.js try using a callback.
user.save(function(err, res){
if (err){throw err;}
console.log('test me', res)
})

You are closing your request before the user being saved.
move the res.end() into then function to solve the problem.
Or you can make an async function(req, res) and put await before user.save(), remove then and wait for database to response, like below:
app.post('/signup', urlencodedParser, async function(req, res) {
let user = new User(req.body);
await user.save();
res.end();
});

Related

Mongoose find not returning the full collection

I am trying to read data from an html form through a POST, store it in a mongoDB and query it using model.find() and print it in console. But when i run this for the first time the find() is returning an empty object and on giving the next input the previous data excluding the current input is retrieved by th find(). How can i print the full collection including the freshly entered data
app.post("/", function(req, res){
postTitle = req.body.postTitle;
postDesc = req.body.postDesc;
const post = new Post({
title:postTitle,
desc:postDesc
});
post.save();
Post.find({}, function(err, data){
if(err){
console.log(err);
}else{
console.log(data);
}
});
//console.log(postTitle, postDesc);
});
The command post.save(); will just begin working and your code will continue meanwhile. When your Post.find({} ... starts working, your post.save(); haven't finished working, and thus you're not getting the results.
Change the function so you wait for the save to give you a callback with an ok and then you can query the database.
app.post("/", function(req, res) {
const postTitle = req.body.postTitle;
const postDesc = req.body.postDesc;
const post = new Post({
title: postTitle,
desc: postDesc
});
post.save(function(err) {
if (err) {
// Something went wrong with the save, log and return the error message
console.error(err);
return res.send(err);
}
console.log(`Post "${postTitle}" saved to database.`);
// Since we know that the post has been saved, continue querying the database.
Post.find({}, function(err, data) {
if (err) {
// Something went wrong with the query, log and return the error message
console.error(err);
return res.send(err);
}
console.log(data);
res.send(data);
});
});
});
This code is not tested.
You can also try async/await out, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function and also mongoose documentation for promises & async/await https://mongoosejs.com/docs/promises.html.
I myself would write the function like this using async/await and es6.
app.post('/', async(req, res) => {
const post = new Post({
title: req.body.postTitle,
desc: req.body.postDesc
});
try {
await post.save();
const posts = await Post.find();
console.log(posts);
} catch (err) {
console.error(err);
}
res.end();
});
You can try with exec
Post.find({}).exec(function (err, d) {
if(err){
console.log(err);
}else{
console.log(d);
}
});
Or try to use async await to make sure your query is running step by step
const user_info = await Post.find({});
This code is not tested
here post.save() is an async function that means it does not complete immediately. You need to use async - await in order to wait for the save() function to finish and then you query the database.

How can I handle runtime errors on Express app?

I'm new to express + node.js, so I'm written an rest api using mongoose. What's the best way to handle runtime errors as database errors etc.?
I've read in the express documentation that you can have a middleware function(err, res, req, next) to handle this errors, and you can call this function only calling next(err). That's ok, so Imagine you have a User moongose model and in a controller you write this function:
const find = (email, password) => {
User.find({ email: email }, (err, doc) => {
if (err) {
// handle error
}
return doc;
});
};
Then, you have in another file a handler for a route:
router.get('/users', (req, res) => {
userController.find(req.body.email);
});
So, at this point, you can handle the mongo error writing throw(err) in the model and using try/catch in the controller to then call next(err) right? But I've read that using try/catch in JavaScript is not a good practice because it creates a new execution context etc.
What's the best way to handle this errors in Express?
I will suggest you to use promises. It not only makes your code cleaner but also error handling is much easier. For reference you can visit this or this.
If you are using mongoose you can plugin your own library of promise.
const mongoose = require('mongoose');
mongoose.connect(uri);
// plug in the promise library:
mongoose.Promise = global.Promise;
mongoose.connection.on('error', (err) => {
console.error(`Mongoose connection error: ${err}`)
process.exit(1)
})
And use it like below:
In controller:
const find = (email) => {
var userQuery = User.find({ email: email });
return userQuery.exec();
};
In Router:
router.get('/users', (req, res) => {
userController.find(req.body.email).then(function(docs){
// Send your response
}).then(null, function(err){
//Handle Error
});
});

Display of data on view from route.js

// This is my router.js
router.get('/restful', function(req, res){
console.log("before");
User.show_deatils(req, res, function(err, resultArray){
if(!err){
req.session.resultArray=resultArray;
req.session.save();
}
});
//console.log(req.session.resultArray)
res.render('restful',{resultArray:req.session.resultArray});
});
//This is model.js
module.exports.show_deatils=function(req,res,callback){
var resultArray=[];
mongo.connect(url,function(err,db){
assert.equal(null,err);
var cursor=db.collection('users').find();
cursor.forEach(function(doc,err){
assert.equal(null,err);
resultArray.push(doc);
callback(null, resultArray);
});
});
}
//I want to display the resultArray on loading view(restful.ejs).But the problem is when I first redirect to the page.It is not showing any value.But upon clicking the same page its been showing me the details.What I feel is on the first load it is not storing the values in session.How can we solve this issue?I am trying this for long time.
That is because you are rendering outside the async callback function
Change your code like this
router.get('/restful', function(req, res){
console.log("before");
User.show_deatils(req, res, function(err, resultArray){
if(!err){
req.session.resultArray=resultArray;
req.session.save();
res.render('restful',{resultArray:req.session.resultArray});
}
});
//console.log(req.session.resultArray)
});
It is storing correct in the session, that part is working but since the first time you are rendering outside the async callback, it has no value.

How to login each time before running a test

I am a newbie in testing in Javascript and trying to test my NodeJS backend using mocha and chai.
How all of my routes are filled with a middleware which does not allow people to go forward if they aren't logged in.
Something like this
app.post('/user/analytics/driverdata', checkauth, function(req, res) {
analytics.driverData(req, res);
});
where checkauth is
var checkauth = function(req, res, next) {
console.log("In checkauth");
if (req.isAuthenticated()) {
next();
} else {
console.log("Doesn't authenticate");
res.status(401);
res.set('Content-Type', 'application/json');
res.end(JSON.stringify({
'success': false
}));
}
};
The isAuthenticated parameter is attached to request by PassportJS, when it deserializes a request.
What I want to do is write a test for
app.post('/user/analytics/driverdata', checkauth, function(req, res) {
analytics.driverData(req, res);
});
this API. in which I am failing as I am not logged in hence unable to reach there.
So I wrote a beforeEach to login the user beforeEach it. It goes like this.
var expect = require('chai').expect;
var request = require('superagent');
beforeEach(function(done){
//login into the system
request
.post("http:localhost:5223/user/authenticate/login")
.send({username : "saras.arya#gmail.com", password : "saras"})
.end(function assert(err, res){
if(err){
console.log(err);
done();
}
else{
done();
}
});
});
I don't know what am I doing wrong and the internet has failed me. Any help to point out where am I going wrong will be appreciated.
After seeing a lot of stuff and fidgeting around I think I finally cracked it. It would be harsh not to mention the answer here which introduced me to the concept of an agent. Which helped me crack this.
Inside your describe block, or probably before the block you can have the following it.
var superagent = require('superagent');
var agent = superagent.agent();
it('should create a user session successfully', function(done) {
agent
.post('http://localhost:5223/user/authenticate/login')
.send({
username: 'whatever#example.com',
password: 'ssh-its-a-secret'
})
.end(function(err, res) {
console.log(res.statusCode);
if (expect(res.statusCode).to.equal(200))
return done();
else {
return done(new Error("The login is not happening"));
}
});
});
the agent variable holds the cookies for you, which are then used by PassportJS to authenticate you.
here is how you do it. So the agent variable is inside a describe. In the same describe inside another it.
it("should test analytics controller", function(done) {
agent.post('http://localhost:5040/user/analytics/driverData')
.send({
startDate: "",
endDate: "",
driverId: ""
})
.end(function(err, res) {
if(!err)
done();
});
});
This function passes like a charm. This was one complete documentation that was missing.

NodeJs - calling one method from another in server controller

With the following controller, how can I call one method from another in the same controller?
Specifically, calling login() within a successful signup(), while retaining the same functionality for login() when it is used by a form?
The line this.login(newUser) does not work, nor does plain old login(newUser)
In both scenarios, I get the error:
TypeError: Cannot call method 'login' of undefined
var mongoskin = require('mongoskin');
module.exports = {
login: (function (req, res) {
req.db.collection('auth').findOne({_id: mongoskin.helper.toObjectID(req.body.id)},
function (err, results) {
// log person in and send results to client
}
)
}),
signup: (function (req, res) {
var user = req.body;
req.db.collection('auth').insert(user, function (err, newUser) {
// after adding user, automatically log them in
// does not work:
//login(newUser, function (err) {
// do something
//})
// does not work:
this.login(newUser, function (err) {
// do something
})
}
)
})
}
Controllers should be doing as little as possible, and should orchestrate the work required by executing functions stored elsewhere.
View this gist - click here
What I have done is created "services" that are not tied to the client request, therefore re-usable everywhere.
Hope this helps.
Thanks to Dave Newton
var mongoskin = require('mongoskin');
var myCollection = 'auth';
Solution
function localLogin(db, myCollection, user, res){
db.collection(myCollection).findOne({_id: mongoskin.helper.toObjectID(user._id)},
function(err, user){
res.send({ token: createToken(user) });
});
module.exports = {
login: (function (req, res) {
var user = req.body;
localLogin(req.db, myCollection, user, res)
},
signup: (function (req, res) {
var user = req.body;
req.db.collection(myCollection).insert(user, function (err, newUser) {
// after adding user, automatically log them in
localLogin(req.db, myCollection, newUser, res)
})
}
) }) }

Resources