app.get("/users/:id", function (req, res) {
User.findById(req.params.id, function (err, foundUser) {
if (err) {
console.log(err)
} else {
console.log(foundUser)
}
})
Item.countDocuments({ UID: req.params.id }, function (err, itemCount) {
if (err) {
console.log(err)
} else {
console.log(itemCount)
}
})
Item.find({ UID: req.params.id }, function (err, foundItems) {
if (err) {
console.log(err)
} else {
console.log(foundItems)
}
})
res.render("users/show", { user: foundUser, newListItems: foundItems, itemCount: itemCount})
For some reason this wont render and keeps saying that the variables dont exist despite the callbacks above. Using EJS for render.
I may be reading the code incorrectly, but aren't the callback variables out of scope for the res.render method?
You may need to return the callbacks from each of the Mongoose queries and store them in variables that are within the scope of the res.render method.
As suggested by Richie, you cannot access the variables from.inside the callbacks in the outer scope
app.get("/users/:id", function (req, res) {
User.findById(req.params.id, function (err, foundUser) {
if (err) { console.log(err) }
else {
console.log(foundUser)
Item.countDocuments({ UID: req.params.id }, function (err, itemCount) {
if (err) { console.log(err) }
else {
console.log(itemCount)
Item.find({ UID: req.params.id }, function (err, foundItems){
if (err) { console.log(err) }
else {
console.log(foundItems);
res.render("users/show", { user: foundUser, newListItems: foundItems, itemCount: itemCount})
}
});
}
});
}
});
});
Notice that I've put the res.render method inside the callbacks so that the variables are available to it.
EDIT
As suggested by Marcel Djaman, You should probably use async/await to make code more readable. Read more about async/await here
app.get("/users/:id", async function (req, res) {
try {
const foundUser = await User.findById(req.params.id);
console.log(foundUser)
const itemCount = await Item.countDocuments({ UID: req.params.id });
console.log(itemCount)
const foundItems = await Item.find({ UID: req.params.id });
console.log(foundItems);
res.render("users/show", { user: foundUser, newListItems: foundItems, itemCount: itemCount});
} catch(err) {
console.error(err)
}
});
You can notice how much simpler this code is than the one above it.
I create the user, hash his password and save on mongo. My problem begins when I try to update that user. For now, when I update the hash isn't generated, cause I really don't know how to do it.
The middleware to get the user that I'm talking about:
exports.userByID = function(req, res, next, id) {
User.findOne(
{
_id: id
},
function(err, user) {
if (err) {
return next(err);
} else {
req.user = user;
next();
}
}
);
};
The user controller, to update an user:
exports.update = async function(req, res, next) {
User.findByIdAndUpdate(req.user.id, req.body, function(err, user) {
if (err) {
return next(err);
} else {
res.json(user);
}
});
};
The pre 'save' on User's model:
UserSchema.pre("save", function(next) {
var user = this;
if (user.password) {
var md5 = crypto.createHash("md5");
user.password = md5.update(user.password).digest("hex");
console.log("Password após o save (hasheando):" + user.password);
}
next();
});
I'm using passport authentication ('local'). Already tried user.save() on the controller update:
user.save();
res.json(user);
But, without success.
This is may be because you are not storing the new_password in the mongo.
In update controller you have to do like this:
User.findByIdAndUpdate(req.user.id, req.body, function (err, user) {
if (err) {
return next(err);
} else {
user.password = req.body.new_password;
user.save(function (err, user) {
if (err) {
res.send("Error: ", err);
} else {
res.send("password updated successfully!");
}
})
}
});
Before saving the password just hash it and update it in DB. it will be something like below.
exports.update = async function(req, res, next) {
let { body} = req;
if(body['password']){
var md5 = crypto.createHash("md5");
body['password']= md5.update(body['password']).digest("hex");
}
let updateUser = await User.findByIdAndUpdate(req.user.id, body)
};
I am new to Node.JS coming from a Java Background I am using express to build this Rest API . What I am trying to do is build the concept of a manager. I am looking for a elegant way of returning a user object in the following:
users route: user.js
router.get('/find/:email', function(req, res, next){
userWare.findUserByEmail(req, res, next)
});
middleware/manager: usermiddleware.js
module.exports = {
findUserByEmail: function(req, res, next) {
models.core_user.find({
where:{
email: req.params.email
}
}).then(function(user){
res.json(user)
}, function(err){
res.status(404).json(err);
});
},
}
So In this above function I would like to return the user object to the route instead of the json. so that I can create the json from the object in the route. The whole point of this manager class will be to fectch and return objects.
What you need to do is call the callback function with the data you need or return the promise.
Callback
user.js
router.get('/find/:email', function (req, res, next) {
userWare.findUserByEmail(req.params.email, function (err, data) {
// error as first parameter or null if no error occurred
if (err) {
return res.status(404).json(err);
}
res.json(user);
});
});
usermiddleware.js
module.exports = {
findUserByEmail: function (email, next) {
models.core_user.find({
where: {
email: email
}
}).then(
function (user) {
// async call of callback with user object
next(null, user);
},
function (err) {
// async call of callback with error
next(err);
}
);
}
};
Promise
You could also just return the promise returned by your model, then it would look like this:
user.js
router.get('/find/:email', function (req, res, next) {
userWare.findUserByEmail(req.params.email).then(
function (user) {
res.json(user);
},
function (err) {
res.status(404).json(err)
}
);
});
usermiddleware.js
module.exports = {
findUserByEmail: function (email) {
// return the promise to the caller
return models.core_user.find({
where: {
email: email
}
});
}
};
I have an ExpressJS app which takes form data and does the following:
1. checks all required values are supplied,
2. validates the data is valid,
3. adds a record to the database to get a unique ID,
4. uses the ID and data to call a separate server,
5. upon response from the server, update the database record with details of the response.
I'm using mongoskin for the database.
My question relates to how I control the flow. Essentially I have written each of the above steps as a middleware function because I need to call next() on the success (or next(err) on error) at each callback.
It seems like I'm writing too much middleware and should be able to group the steps into larger sets of middleware containing multiple 'sub-functions' but I'm not sure how to do this in Express since I need to call next() every time an async function call completes. Is there a correct way to do this or is this 'one middleware per step' approach really the right way to run this?
EDIT: Posting some code as requested. This is partial code for the sake of brevity:
function validateFields(req, res, next) {
//...
//iterate over req.body to confirm all fields provided
//...
if (allDataProvided) {
//...
//iterate over req.body to confirm all fields valid
//...
if (allDataValid) {
return(next());
} else {
return(next(err));
}
} else {
return(next(err));
}
},
//get an auto incrementing ID fields from a mongodb collection (counters)
function getNextID(req, res, next) {
counters.findAndModify(
{ _id: "receiptid" },
[['_id','asc']],
{ $inc: { seq: 1 } },
{},
function(err, doc) {
if (err) {
return next(err);
} else {
req.receiptid = doc.seq;
return next();
}
});
},
//insert a new record into the transaction collection (txns) using the new ID
function createTransaction(req, res, next) {
txns.insert(
{ _id : req.receiptid,
body : req.body,
status : "pending"},
{},
function(err, r) {
if (err) {
return next(err);
} else {
return next();
}
});
},
//process the data on the remote web service using the provider's API (remoteapi)
function processTransaction(req, res, next) {
remoteapi.processTransaction(
{ data: req.body,
receiptid: req.receiptid },
function(err, r) {
if (err) {
return next(err);
} else {
req.txnReceipt = r;
return next();
}
});
},
//update the record in the database collection (txns) with the server response
function updateDatabase(req, res, next) {
txns.updateById(req.receiptid,
{ $set :{status : "success",
receipt: req.txnReceipt }
}, function (err, r) {
if (err) {
return next(err);
} else {
return next();
}
});
}
And as it currently stands with the above functions, my route which utilises this middleware starts like this:
router.post('/doTransaction',
validateFields,
getNextID,
createTransaction,
processTransaction,
updateDatabase,
function(req, res, next) { //...
It seems like I should be able to create one middleware function which does all of these things in a row without each having to be a separate middleware, but since each middleware has an async function in it and I need to call next() in the resulting callback, this is the only way I can see it working.
Thanks
Aaron
It's fairly easy to implement all your steps in one middleware. I've included some pseudo-code below (that makes various assumptions on how your code is structured, because you didn't provide implementation details, but it's just to give an idea).
It uses the on-headers package to "catch" responses.
var onHeaders = require('on-headers')
// Your middleware function
app.use(function(req, res, next) {
// Update the database when the response is being sent back.
onHeaders(res, function() {
// Do database update if we have a document id.
if (req._newDocumentId) {
db.collection.update(req._newDocumentId, data, function() {
// can't do a lot here!
});
}
});
// Perform the requires steps
if (! checkValuesAreSupplied(req)) {
return next(new Error(...));
}
if (! validateValues(req)) {
return next(new Error(...));
}
// Insert into database.
db.collection.insert(data, function(err, doc) {
if (err) return next(err);
...process the newly created doc...
// Store _id in the request for later.
req._newDocumentId = doc._id;
// Make the call to the separate server
makeCallToOtherServer(otherData, function(err, response) {
if (err) return next(err);
...process response...
return next();
});
});
});
You can put everything in one module and just use callbacks to go trought each step but in this case you can get "callback hell".
So I can suggest the async npm package which I think the better way.
using this library your code will look like:
function allInOneMiddleware(req, res, next) {
async.waterfall([
function (callback) {
validateFields(req, res, callback);
},
getNextID,
createTransaction,
processTransaction,
updateDatabase
], function (err) {
if (err) {
return next(err);
}
// response?
});
}
function validateFields(req, res, callback) {
//...
//iterate over req.body to confirm all fields provided
//...
if (allDataProvided) {
//...
//iterate over req.body to confirm all fields valid
//...
if (allDataValid) {
return callback(null, req.body);
}
return callback(err);
}
return callback(err);
}
//get an auto incrementing ID fields from a mongodb collection (counters)
function getNextID(body, callback) {
counters.findAndModify(
{_id: "receiptid"},
[['_id', 'asc']],
{$inc: {seq: 1}},
{},
function (err, doc) {
if (err) {
return callback(err);
}
callback(null, body, doc.seq);
});
}
//insert a new record into the transaction collection (txns) using the new ID
function createTransaction(body, receiptid, callback) {
txns.insert(
{
_id: receiptid,
body: body,
status: "pending"
},
{},
function (err, r) {
if (err) {
return callback(err);
}
callback(null, body, receiptid);
});
}
//process the data on the remote web service using the provider's API (remoteapi)
function processTransaction(body, receiptid, callback) {
remoteapi.processTransaction(
{
data: body,
receiptid: receiptid
},
function (err, r) {
if (err) {
return callback(err);
}
callback(null, receiptid, r);
});
}
//update the record in the database collection (txns) with the server response
function updateDatabase(receiptid, txnReceipt, callback) {
txns.updateById(receiptid,
{
$set: {
status: "success",
receipt: txnReceipt
}
}, callback);
}
Thanks Nicolai and robertklep for the answers. Whilst I think both answers do answer the question, I realised as I was working through this myself that I had failed to see the forest for the trees.
I could just pass the next function through each callback function until I reached the final one and call it to pass the control back to the middleware stack. This also allows me to simply call next(err) inside any of those functions.
So my answer is very similar to the concept outlined by Nicolai except I don't think I need to use the async package in this case because I don't feel like this particular case took me to callback hell.
Here is my answer to my own question:
function validateFields(req, res, next) {
//...
//iterate over req.body to confirm all fields provided
//...
if (allDataProvided) {
//...
//iterate over req.body to confirm all fields valid
//...
if (allDataValid) {
getNextID(req, res, next)
} else {
return(next(err));
}
} else {
return(next(err));
}
},
//get an auto incrementing ID fields from a mongodb collection (counters)
function getNextID(req, res, next) {
counters.findAndModify(
{ _id: "receiptid" },
[['_id','asc']],
{ $inc: { seq: 1 } },
{},
function(err, doc) {
if (err) {
return next(err);
} else {
req.receiptid = doc.seq;
createTransaction(req, res, next);
}
});
},
//insert a new record into the transaction collection (txns) using the new ID
function createTransaction(req, res, next) {
txns.insert(
{ _id : req.receiptid,
body : req.body,
status : "pending"},
{},
function(err, r) {
if (err) {
return next(err);
} else {
processTransaction(req, res, next);
}
});
},
//process the data on the remote web service using the provider's API (remoteapi)
function processTransaction(req, res, next) {
remoteapi.processTransaction(
{ data: req.body,
receiptid: req.receiptid },
function(err, r) {
if (err) {
return next(err);
} else {
req.txnReceipt = r;
updateDatabase(req, res, next);
}
});
},
//update the record in the database collection (txns) with the server response
function updateDatabase(req, res, next) {
txns.updateById(req.receiptid,
{ $set :{status : "success",
receipt: req.txnReceipt }
}, function (err, r) {
if (err) {
return next(err);
} else {
return next();
}
});
}
So instead of calling next() on successful completion of each async function and having to write another middleware for the next step, I simply pass next on to the next function until it's required.
This was, I can just call the first function as my middleware, like this:
router.post('/doTransaction',
validateFields,
function(req, res, next) { //...
and in turn, the remaining steps are called in sequence when each action completes.
I'm a beginner in Node/Express. I tried to make an CRUD application but stuck at update and delete. I think my router code is problematic but I don't know why. The following code is in my controller, everything works but PUT and DELETE. It always route to GET. I tried to use next(); but it returns this error: Can't set headers after they are sent..
I can make the delete works by using GET /:company_id/delete but it's not a good and standardized solution. How can I get update and delete process worked?
'use strict';
var Companies = require('../../models/companies');
module.exports = function (router) {
// INDEX
// accessed at GET http://localhost:8000/companies
router.get('/', function (req, res) {
Companies.find(function(err, model) {
if (err) {
res.send(err);
}
else {
res.format({
json: function () {
res.json(model);
},
html: function () {
res.render('companies/index', model);
}
});
}
});
});
// CREATE VIEW
// accessed at GET http://localhost:8000/companies/create
router.get('/create', function (req, res) {
res.render('companies/create');
});
// CREATE DATA
// accessed at POST http://localhost:8000/companies
router.post('/', function (req, res) {
var name = req.body.name && req.body.name.trim();
var type = req.body.type && req.body.type.trim();
// VALIDATION
if (name === '') {
res.redirect('/companies/create');
return;
}
var model = new Companies({name: name, type: type});
model.save(function(err) {
if (err) {
res.send(err);
}
else {
res.redirect('/companies');
}
});
});
// READ
// accessed at GET http://localhost:8000/companies/:company_id
router.get('/:company_id', function(req, res) {
Companies.findById(req.params.company_id, function(err, model) {
if (err) {
res.send(err);
}
else {
res.render('companies/read', model);
}
});
});
// UPDATE VIEW
// accessed at GET http://localhost:8000/companies/:company_id/edit
router.get('/:company_id/edit', function(req, res) {
Companies.findById(req.params.company_id, function(err, model) {
if (err) {
res.send(err);
}
else {
res.render('companies/edit', model);
}
});
});
// UPDATE DATA
// accessed at PUT http://localhost:8000/companies/:company_id
router.put('/:company_id', function(req, res) {
Companies.findById(req.params.company_id, function(err, model) {
if (err) {
res.send(err);
}
else {
model.name = req.body.name;
model.type = req.body.type;
model.save(function(err) {
if (err) {
res.send(err);
}
else {
res.redirect('/companies');
}
});
}
});
});
// DELETE
// accessed at DELETE http://localhost:8000/companies/:company_id
router.delete('/:company_id', function (req, res) {
Companies.remove({ _id: req.params.company_id }, function(err) {
if (err) {
res.send(err);
}
else {
res.redirect('/companies');
}
});
});
};
HTML forms only support GET and POST. XMLHTTPRequest supports PUT and DELETE however, so you may have to go that route OR use something like method-override to allow HTML forms to submit using other HTTP verbs.