Very similar Node routes - any better solution? - node.js

I have two very similar routes (two API results). So one for grabbing the email address and one for grabbing their username. I'm using these two separately, one for validating the email address by using an AJAX call and another for validating the username (basically checking if either exist).
Is there any way in Node/Express/Mongoose to specifically check both (at seperate times) without having to have two methods, it just feels so redundant?
Used to check for email validation (that it doesn't already exist)
router.get('/:id', function(req, res) {
var emailAddress = req.params.id;
User.find({ 'emailAddress': emailAddress }, function (err, user) {
if(!user) {
res.statusCode = 404;
return res.json({
error: 'Not found'
});
}
if (!err) {
if (user[0]!=undefined) {
return res.json(true);
} else {
return res.json({
error: 'Not found'
});
}
} else {
res.statusCode = 500;
log.error('Internal error(%d): %s', res.statusCode, err.message);
return res.json({
error: 'Server error'
});
}
});
});
Used to check for username validation (that it doesn't already exist)
router.get('/username/:id', function(req, res) {
var username = req.params.id;
User.find({ 'username': username }, function (err, user) {
if(!user) {
res.statusCode = 404;
return res.json({
error: 'Not found'
});
}
if (!err) {
if (user[0]!=undefined) {
return res.json(true);
} else {
return res.json({
error: 'Not found'
});
}
} else {
res.statusCode = 500;
log.error('Internal error(%d): %s', res.statusCode, err.message);
return res.json({
error: 'Server error'
});
}
});
});
Just returning boolean's essentially for both results. Surely there's a better way than this? There's just so much duplicated code it's frustrating.
Edit: still researching but maybe I could use User.find() and pass in what I want but in terms of passing that from the API to the user Model, how is that possible?

Split them in to reusable units and use them in the routes.
function isUserExists(query, callback) {
User.findOne(query, function(error, user) {
if(error) {
return callback(error);
}
callback(null, !!user);
});
}
function sendResponse(res, error, result) {
if(error) {
res.statusCode = 500;
log.error('Internal error(%d): %s', res.statusCode, err.message);
return res.json({error: 'Server error'});
}
if(!user) {
res.statusCode = 404;
return res.json({
error: 'Not found'
});
}
res.json(result);
}
router.get('/:id', function(req, res) {
isUserExists({emailAddress: req.params.id}, sendResponse.bind(null, res));
});
router.get('/username/:id', function(req, res) {
isUserExists({username: req.params.id}, sendResponse.bind(null, res));
});

Related

Node Express.js Middleware, end Response

validateRegister: async (req, res, next) => {
UserModel.findOne({email:req.body.email}, (err, example) => {
console.log(example);
if(err) console.log(err);
if(example) {
res.status(400).json({message: "Email already registered!"});
res.end() //next('route')
}
});
console.log("test");
const user = new UserModel(req.body);
await user.save((err) => {
if (err) return res.status(500).json({ message: "Database issue!" });
});
next();
},
Ok, I tried to insert user data if it is not already in the database using mongoose. If the User regarding the email is already in the database the response should be ended and the user not inserted. I tried to end the response with res.end() and next('route'), but nothing seems to work, the console.log("test") still runs.
Error:
events.js:353
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:561:11)
Thanks for your help
Code below callback function gets executed before callback gets completed and multiple res.send happened.
you can try this
validateRegister: async (req, res, next) => {
UserModel.findOne({ email: req.body.email }, (err, example) => {
console.log(example);
if (err) {
console.log(err);
return res.status(500).json({ message: "Something went wrong" });
}
if (example) {
return res.status(400).json({ message: "Email already registered!" });
}
console.log("test");
const user = new UserModel(req.body);
await user.save((err) => {
if (err) return res.status(500).json({ message: "Database issue!" });
});
});
next();
}
Or
validateRegister: async (req, res, next) => {
try {
let example = await UserModel.findOne({ email: req.body.email });
console.log(example);
if (example)
return res.status(400).json({ message: "Email already registered!" });
console.log("test");
const user = new UserModel(req.body);
await user.save((err) => {
if (err) return res.status(500).json({ message: "Database issue!" });
});
next();
} catch (err) {
console.log(err);
return res.status(500).json({ message: "Something went wrong" });
}
}
you can add return before returning response in the case of user email already found.
What seems to happen is that your program is calling res two times

router.patch is returning 404 "not found"

I am working on small node api and I have an issue with patch method.
My router.patch is returning me 404.
This is how my route looks:
router.param('userId', findById);
router.patch(
'/api/projects/update/:projectId/:userId',
authCheck,
isAdmin,
findProjectById,
update
);
The findById is based on my :userId param. Whole method looks like this:
exports.findById = async (req, res, next) => {
try {
let user = await User.findById(req.params.userId);
if (!user) return res.status(400).json({ msg: 'User not found' });
next();
} catch (err) {
console.error(err.message);
if (err.kind === 'ObjectId') {
return res.status(400).json({ msg: 'User not found' });
}
res.status(500).send('Server Error');
}
};
Based on that I should get proper user for proper project.
My two ayhorization methods:
exports.authCheck = async (req, res, next) => {
try {
/* get token from header
replace('Bearer', '') - this will remove bearer from token header
*/
const token = req.header('Authorization').replace('Bearer', '');
//check if no token
if (!token) {
return res.status(401).json({ msg: 'No token, authorization denied' });
}
/*
decoded contains _id as a payload in token. Id is from getAuthToken */
const decoded = jwt.verify(token, config.get('jwtSecret'));
const user = await User.findOne({
_id: decoded._id,
'tokens.token': token,
});
if (!user) {
throw new Error();
}
req.token = token;
req.user = user;
next();
} catch (err) {
res.status(401).json({ msg: 'Please authenticate' });
}
};
exports.isAdmin = async (req, res, next) => {
try {
if (req.user.role !== config.get('roleSecret')) {
return res.status(403).json({
errors: [
{
msg: 'No Admin rights. Access Denied!!',
},
],
});
}
next();
} catch (err) {
res.status(403).json({ msg: 'Forbidden access' });
}
};
Finaly, my project controller where i have findProjectById, update
In findProjectById I am looking for project based on route param and i assing it to project
exports.findProjectById = async (req, res, next) => {
const _id = req.params.projectId;
try {
let project = await Project.findById(_id);
if (!project) return res.status(400).json({ msg: 'Porject not found' });
req.project = project;
next();
} catch (err) {
console.error(err.message);
if (err.kind === 'ObjectId') {
return res.status(400).json({ msg: 'Porject not found' });
}
res.status(500).send('Server Error');
}
};
My update method i s not done, because i was testing if anything heppens
exports.update = async (req, res) => {
try {
const proj = await req.project;
const _id = proj._id;
await Project.findByIdAndUpdate(_id, req.body, {
new: true,
runValidators: true,
});
if (!proj) {
return res.status(404).json({ msg: 'Project not found' });
}
return res.json(proj);
} catch (err) {
res.status(500).send('Server Error');
}
};
Not sure what am I missing here, but after few hours and lot of searching still can't get this working
Get this working. Issue was in my router path.
/api/projects/update/:projectId/:userId
Should be
/projects/update/:projectId/:userId
this can be closed

Call a function from different controller in express

I have a function in questionController
show: function (req, res) {
var id = req.params.id;
questionModel.findOne({_id: id}, function (err, question) {
if (err) {
return res.status(500).json({
message: 'Error when getting question.',
error: err
});
}
if (!question) {
return res.status(404).json({
message: 'No such question'
});
}
//var user = userController.show();
return res.render("questions/question", question);
});
},
And i want to somehow use the function from userController so that i would get the user that posted the question. This is the function i want to use:
show: function (req, res) {
var id = req.params.id;
userModel.findOne({_id: id}, function (err, user) {
if (err) {
return res.status(500).json({
message: 'Error when getting user.',
error: err
});
}
if (!user) {
return res.status(404).json({
message: 'No such user'
});
}
return res.json(user);
});
},
Or is there a better way i can achieve this? I should note, i usually use relationship databases.
Code i wish to achieve (in a more elegant way if possible):
//this is a questionController function
show: function (req, res) {
var id = req.params.id;
questionModel.findOne({_id: id}, function (err, question) {
if (err) {
return res.status(500).json({
message: 'Error when getting question.',
error: err
});
}
if (!question) {
return res.status(404).json({
message: 'No such question'
});
}
var userID = question.userID;
//this is a function i copied from userController that i would like to call from userController so it would be a bit more elegant
userModel.findOne({_id: userID}, function (err, user) {
if (err) {
return res.status(500).json({
message: 'Error when getting user.',
error: err
});
}
if (!user) {
return res.status(404).json({
message: 'No such user'
});
}
question.username = user.username;
return res.render("questions/question", question);
});
});
},
I believe I understood what you want, please, let me know if got it right.
Try this:
Answer: you must use mySchema.methods
In the same file you are defining your User schema, before mongoose.model(...) (i.e., before you compile the model):
UserSchema.methods.doYourThing(id){
//--------------------------------------------------------
//this is just the function you had to copy, and want to use
this.model("User").findOne({_id: id}, function (err, user) {
if (err) {
return res.status(500).json({
message: 'Error when getting user.',
error: err
});
}
if (!user) {
return res.status(404).json({
message: 'No such user'
});
}
return res.json(user);
});
},
}
Then, when you need to use it, you could do something like:
const user= mongoose.model("User")
..........
user.doYourThing(userid);
I have done the code from my head (i.e., I did not test it), and I do not have information about your schema; you will need to fix my typos and/or adapt to your code; I hope you get the idea.
References
Mongoose "this.model is not a function"
https://mongoosejs.com/docs/models.html
https://mongoosejs.com/docs/guide.html#methods
You should try to import the controller and then call the function;
const userController = require("userController");
userController.show();

Updating a object in mongoose results in loop

In my nodejs API app I have this route:
router.post('/startuserseries', function(req, res, next){
if(!req.body.username){
return res.status(400).json({message: 'Geen username'});
}
User.findOne({ 'username': req.body.username}, function(err, foundUser){
if(err)
return next(err);
if (foundUser) // check the value returned for undefined
{
foundUser.isdoingchallenges = true;
foundUser.save(function (err) {
if(err) {
console.error('ERROR!');
}
});
}
});
});
When I call this route in postman, the request never ends.
I have tried to use PUT but also didn't work, I tried various structures of code but neither worked.
This request will not finish because it doesn't write a response command on server.
You should solve easily this problem like below:
router.post('/startuserseries', function(req, res, next){
if(!req.body.username){
return res.status(400).json({message: 'Geen username'});
}
User.findOne({ 'username': req.body.username}, function(err, foundUser){
if(err)
return next(err);
if (foundUser) // check the value returned for undefined
{
foundUser.isdoingchallenges = true;
foundUser.save(function (err) {
if(err) {
res.json(err);
}
});
}
res.send(200);
// or your specific result json object
// res.json({"error":false,"message":"completed"})
});
});

How to update subdoc data in Mongoose

I am trying to update user.task.fixitItem, where the Task schema is embedded within the User schema.
Using this get,
app.get('/api/tasks/:id/edit', isAuthenticated, function (req, res) {
console.log('*** testing route GET /api/tasks/:id/edit', req.params);
User.findOne({'task._id': req.params.id})
.select('task.$')
.exec(function(err, user) {
if(!user) {
res.statusCode = 404;
return res.send({ error: 'Not found' });
}
if(!err) {
return res.render('tasks/edit', {task: user.task[0] });
} else {
res.statusCode = 500;
console.log('Internal error(%d): %s', res.statusCode, err.message);
return res.send({ error: 'Server error' });
}
}
);
});
How do you write the put to update the data?
You need to use the update method with $set
User.update(
{ 'task._id': req.params.id },
{ $set: { 'task.$.fixitItem': 'new value' }},
function(err, user) {
}
);

Resources