Call a function from different controller in express - node.js

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();

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

Mongoose , response is not correct after remove

How can I get a full data except deleted, after delete?
Project.findOneAndRemove({_id: projectID, name: projectName},
function(err, project){
if (err) {
return res.json({message: 'Error on the server!', status: 500 });
}
// Here I need a full data except deleted one
console.log(project)
return res.json({project, status:200});
}
)
or Do I find again inside success callback to get full data?
Project.find({}, function(err, projects){
if (err) return res.json({message: 'Error on the server!', status: 500 });
return res.json(projects);
});
This might help you.
router.post('/deleteAndReturn', async (req, res) => {
try {
await Project.findOneAndRemove({ _id: projectId })
const projects = await Project.find({})
return res.status(200).json(projects)
} catch (err) {
res.status(500).send("Server Error")
}
})

Repeating code for same operation of different datatypes, Express(nodejs) app with mongoose

I am creating an Express rest API that connects to a MongoDB with mongoose. The database has several different datatypes: User, Book, Food and so on. As I am writing endpoints and function for these datatypes. I realized that I am repeating a lot of the same code, for example, for the code below:
// Book.js
import Book from './models/Book'
function deleteById (req, res, next) {
Book.findByIdAndRemove(req.params.id, function (err) {
if (err) {
let error = new Error('Failed to delete an book in database')
error.statusCode = 500
return next(error)
}
res.json({
success: true,
message: 'Book deleted successfully'
})
})
}
I can just change Book to User or Food and the same code can be used. Which results in a lot of repeating code that looks like this:
// Food.js
import Food from './models/Food'
function deleteById (req, res, next) {
Food.findByIdAndRemove(req.params.id, function (err) {
if (err) {
let error = new Error('Failed to delete an food in database')
error.statusCode = 500
return next(error)
}
res.json({
success: true,
message: 'Food deleted successfully'
})
})
}
So I was wondering if there is a way to generate the functions so that I don't need to repeat the code for the deleteById function for each type of data. What would be the best practice? Or is it a bad practice to try to generate the functions and repeating is necessary?
Any help would be really appreciated, thanks in advance.
You can create a delete middleware. In the below code I pass the model delete function as argument and I return a middleware. In that middleware function I call the delete function that came dynamically.
var deleteMiddleWare = function(deleteByIdFunc, name) {
var deleteId = req.params.id;
if (!deleteId)
res.json({
success: false,
message: `Please provide ${name} id.`
})
return function(req, res, next) {
deleteByIdFunc(req.params.id, function(err) {
if (err) {
let error = new Error(`Failed to delete ${name} in database`)
error.statusCode = 500
return next(error)
}
res.json({
success: true,
message: `${name} deleted successfully`
})
})
}
}
router.get(/user/delete /: id, deleteMiddleWare(User.deleteById, 'User'))
router.get(/food/delete /: id, deleteMiddleWare(Food.deleteById, 'Food'))
After a lot of research, I found the best way to be using a class constructor and pass in datatype as a variable:
class BaseController {
constructor (type, name) {
if (!type || !access) {
throw new Error('Must define datatype')
}
this.datatype = type
this.name = name
this.deleteById = this.deleteById.bind(this)
}
deleteById (req, res, next) {
let name = this.name
this.datatype.findByIdAndRemove(id, function (err) {
if (err) {
let error = new Error(`Failed to delete ${name} in database`)
error.statusCode = 500
return next(error)
}
res.json({
success: true,
message: `${name} deleted successfully`
})
})
}
}
And in the router file, we can just call
const food = new BaseController(Food, 'Food')
router.get('/food/delete/:id', food.deleteById)

Very similar Node routes - any better solution?

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));
});

NodeJS, Is it a bad practise to return res.json

I'm building a ExpressJS application with NodeJS. My question is there any performance difference if I do
app.get('/test', function(req, res) {
fn(function(err, data) {
if (err) {
return res.json(400, {
error: 1,
msg: "some error"
});
}
///more code
});
});
instead of
app.get('/test', function(req, res) {
fn(function(err, data) {
if (err) {
res.json(400, {
error: 1,
msg: "some error"
});
return;
}
///more code
});
});
Do returning the res variable make any addition load on the server. Both codes work, just the first looks better to me and I save 1 line.
On the contrary, I think many would tell you this sort of idiom is a very sound practice as it makes clear to the reader (often your future self) that you are exiting). What is very nice about the strategy in this particular case is that you can save a bit more code since you now only have a single statement in your conditional branch, which means you can lose some curly braces.
app.get('/test', function(req, res) {
fn(function(err, data) {
if (err) return res.json(400, {
error: 1,
msg: "some error"
});
///more code
});
});
But you asked if there was a performance difference. If there is, I think it would be all but imperceptible.
Returning an object in a function don't make additional load.
On your exemple, based on callback function, there is no difference.
But what if app.get return a Promise ?
This code will provide an Unhandled rejection Error
app.get('/test')
.then( (data) =>
{ /* do something with your data that can throw a functional error
for exemple request for a user on your database based on your data */
if (!user) res.json(401, {msg: 'USER NOT FOUND'});
if (user.someProperty) //will throw an error when user is not found
res.json(200, {msg: 'USER DID IT'});
})
.catch( (err) => {
res.json(500, {msg: 'OUTCH'});
throw(err);
});
This code will not
app.get('/test')
.then( (data) =>
{ /* do something with your data that can throw a functional error
for exemple request for a user on your database based on your data */
if (!user) return res.json(401, {msg: 'USER NOT FOUND'});
if (user.someProperty) //will not be evaluate when user is not found
return res.json(200, {msg: 'USER DID IT'});
})
.catch( (err) => {
res.json(500, {msg: 'OUTCH'});
throw(err);
});
When using promise always return ;)

Resources