unable to add property to the json object - node.js

I am trying to add status to a response on successful update but I am not able to add the status property to json object of form. Here is my code
apiRouter.post('/forms/update', function(req, res){
if(req.body.id !== 'undefined' && req.body.id){
var condition = {'_id':req.body.id};
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form;
objForm.status = "saved successfully";
return res.send(objForm);
});
}else{
res.send("Requires form id");
}
});
and here is the response that I get, notice status is missing
{
"_id": "5580ab2045d6866f0e95da5f",
"test": "myname",
"data": "{\"name\":3321112,\"sdfsd\"344}",
"__v": 0,
"id": "5580ab2045d6866f0e95da5f"
}
I am not sure what I am missing.

Try to .toObject() the form:
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form.toObject();
objForm.status = "saved successfully";
return res.send(objForm);
});

Mongoose query result are not extensible (object are frozen or sealed), so you can't add more properties. To avoid that, you need to create a copy of the object and manipulate it:
var objectForm = Object.create(form);
objectForm.status = 'ok';
Update: My answer is old and worked fine, but i will put the same using ES6 syntax
const objectForm = Object.create({}, form, { status: 'ok' });
Another way using spread operator:
const objectForm = { ...form, status: 'ok' }

Try changing res.send(objForm) to res.send(JSON.stringify(objForm)). My suspicion is that the the Mongoose model has a custom toJson function so that when you are returning it, it is transforming the response in some way.
Hopefully the above helps.

Create empty object and add all properties to it:
const data = {};
data._id = yourObject._id; // etc
data.status = "whatever";
return res.send(data);

Just create a container.
array = {};
Model.findOneAndUpdate(condition, function(err, docs){
array = docs;
array[0].someField ="Other";
});

Related

node js SQL Insert Query with Constructor / JSON text as values parameter

First real app and I'm stuck trying to insert a new row into SQL.
//This is the API endpoint
Sponsor.create = (newSponsor, result)=> {
var sqlStmt = 'insert into sponsors (SponsorName, SponsorState, SponsorDesc, pnfpExperience, Status) values ?'
sql.query(sqlStmt, [newSponsor], (err, res)=> {
if(err) {
console.log(" error: ", err);
result(null, err);
}
else{
console.log(res);
result(null, res);
}
});
};
request.body returns the JSON text but not an array of values that can be used in the query. I keep getting a COUNT error or syntax error. I've tried JSON.parse but I get an undefined error. I know there has to be a simple way to convert the JSON text to the right format but I don't know what I'm missing. The "findAll" and "findById" routes work and I can insert static values. I've also tried "newSponsor" with and without brackets. I'll refine the connection method in time but I'm just trying to understand why this doesn't work. Any help is greatly appreciated.
// Controller.js for Reference
exports.create = function(req, res) {
const new_sponsor = new Sponsor(req.body);
//handles null error
if(req.body.constructor === Object && Object.keys(req.body).length === 0){
res.status(400).send({ error:true, message: 'Please provide all required field' });
}else{
Sponsor.create(new_sponsor, function(err, sponsor) {
if (err)
res.send(err);
res.json({error:false,message:"Sponsor added successfully!",data:sponsor});
});
}
};

NodeJS API GET by Id. How to detect item not found?

I'm developing a GET endpoint to fetch elements from a database(dynamoDB). I'm using Swagger to define the data model in my api. This is the operationId method in my controller:
getInvoiceConfigById: function(req, res) {
var myId = req.swagger.params.id.value;
// InvoiceConfig is a dynamoDb model
// providerId attribute is the unique key of the db table
InvoiceConfig.scan('providerId')
.eq(myId)
.exec(function (err, config) {
if (err) {
console.log("Scan InvoiceConfig error");
throw err;
}
res.status(200).send(config);
});
}
I would like to send a 404 message if the id was not found.
I've noticed in swagger-ui that the body of the response comes empty
Response Body
[]
when the id is not found in the db.
How can I detect in my code when id was not found? I've tried to check if the body of the response is empty:
if(!(config.body))
but this doesn't work because the body is not null
You can check the count of config at server side, if config count is 0 the send desire response code 404 else return 200 response code with data as shown below
getInvoiceConfigById: function(req, res) {
var myId = req.swagger.params.id.value;
// InvoiceConfig is a dynamoDb model
// providerId attribute is the unique key of the db table
InvoiceConfig.scan('providerId')
.eq(myId)
.exec(function (err, config) {
if (err) {
console.log("Scan InvoiceConfig error");
throw err;
}
if (config.length == 0){
res.status(404).end();
} else {
res.status(200).send(config);
}
});
}
Try adding a length check in your callback, like so:
getInvoiceConfigById: function(req, res) {
var myId = req.swagger.params.id.value;
// InvoiceConfig is a dynamoDb model
// providerId attribute is the unique key of the db table
InvoiceConfig.scan('providerId')
.eq(myId)
.exec(function (err, config) {
if (err) {
console.log("Scan InvoiceConfig error");
throw err;
}
if(typeof config === 'array' && 0 < config.length){
res.status(200).send(config);
} else {
res.status(404).send();
}
});
}
I would also suggest that you should simply use the getItem query instead of scan:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#getItem-property
Since the config object keys value is one when result is not found so what you can do is check the length of keys of that object like this :
if ( Object.keys(config).length == 1 )return res.status(400).send("Error 404");

Why can't Restful pass body into database

I'm creating a RESTful API.
I wanna use GET method to check if lastName exists. If it can find lastName, return "YES", otherwise, call a POST method to create a data with lastName entered.
The problem is that it can create a new data, but the body is empty. Ideally, it should contain a value with lastName, like "lastName": "James",
{
"_id": "58a22c3c3f07b1fc455333a5",
"__v": 0
}
Here is my code.
router.route("/findLastName/:id")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.id}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
res.json(response);
}
if (result) {
response = "YES";
res.send(response);
} else {
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
var POSTurl = "http://localhost:6002/users";
var params = "lastName=" + req.params.id;
xhr.open("POST", POSTurl, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(params);
}
});
})
PS: GET method works well, not a issue.
Let me modify a bit of your code and add comments as pointers:
// changed findLastName to find-last-name. It's a common convention,
// urls need to be case insensitive. It doesn't concern lastName, as
// that's a parameter, internal to your app so it's fine.
// even better if you name the route `find-or-create` or something, to better
// reflect what you're doing.
router.route("/find-last-name/:lastName")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.lastName}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
// Adding a `return statement here. If you don't return, you'll tell
// the user that there was an error, but your code continues running
// potentially calling that res.json twice.
// Also, since it's an internal error, it's common to tell the client
// about it, by setting the status to 500
return res.status(500).json(response);
}
if (result) {
// turning the message to JSON instead. You started that above,
// and for the sake of your clients (your frontend), it's
// better to stick to JSON. Also you can pass useful info, such as
// _id of the document.
// Again adding a `return` here, and then the rest of the code
// is nested one level less. not required, but some people like that.
response = {
message: "Last name exists."
};
return res.json(response);
}
// Here begins the new code. I'm typing what I can infer from your code,
// I don't know if your MongoDB driver looks like that exactly.
mongoOp.insert({
deviceId: req.params.lastName
// add other optional properties here.
}, function (err, response) {
if (err) {
var message = {
error: true,
message: 'Cannot save new entry.'
}
return res.status(500).json(message);
}
// if we're here, everything went ok. You can probably return
// the _id of the given user.
return res.json({
message: 'New user created.',
_id: response._id
});
});
});
})

Node JS mongoose Query is not producing result

Been stuck on this problem for hours. The below code:
router.route("/contact")
.get(function(req,res){
var response = {};
Contact.find({},function(err,data){
if(err) {
response = {"error" : "Error fetching data"};
} else {
response = {"message" : data};
}
res.json(response);
});
})
Above query produces result with all the contacts in the database but
router.route("/contact/department/:dept")
.get(function(req,res){
var response = {};
var arrDept = req.params.dept.split(",");
if(arrDept.length == 0){
response = {"error": " Please enter Department keywords"};
}
else{
response = {};
arrDept.forEach(function(currentValue){
Video.find({dept: '/'+currentValue+'/i'}, function(err, data){
if(err){
response[currentValue] = "No data found";
}else{
response[currentValue] = data;
}
});
});
}
res.json(response);
});
this code does not produce any output.
The code is entering the forEach loop in the else block but the query is not generating any result even if I modify the query to a basic one like
Video.find({},function(err, data){
if(err){
response[currentValue] = "No data found";
}else{
response[currentValue] = data;
}
});
The response JSON is still returned blank.
PS: Problem has been simplified as is only an example of actual problem i am facing in the code.
update after answer found.
res.json(response)
was written outside the query that is why a blank json was getting returned. The above scenario can be solved using promise as follows:
var promise = Contact.find({ dept: { $in: arrayDept }}).exec();
promise.then(function(resultJson) { res.json(resultJson); });
This can be used to execute all the queries in the array and return the complete json.
Your res.json(response); in the non-working example is outside the callback of your MongoDB query, that is you are writing the response before the query's callback was actually executed and therefore your result is empty.

NodeJS + Mongoose: Updating all fields on a Mongoose model

I'm building out an api using Node, MongoDB and Mongoose. One thing that is bugging me is that you can't seem to set multiple fields at once:
app.put('/record/:id', function(req, res) {
Record.findById(req.params.id, function(err, doc) {
if (!err) {
doc.update(req.params);
doc.save();
...
However, it seems that you have to work out the update query and run it on the Model object rather than on the document object. Unless you want to assign individual properties and run save() at the end.
Is there any way of accomplishing this without having to write a Mongo query?
jsaak's answer is good but doesn't work for nested objects. I elaborated on his answer by searching and setting nested objects.
I added these functions to a utility.js file
var _ = require('underscore');
exports.updateDocument = function(doc, SchemaTarget, data) {
for (var field in SchemaTarget.schema.paths) {
if ((field !== '_id') && (field !== '__v')) {
var newValue = getObjValue(field, data);
console.log('data[' + field + '] = ' + newValue);
if (newValue !== undefined) {
setObjValue(field, doc, newValue);
}
}
}
return doc;
};
function getObjValue(field, data) {
return _.reduce(field.split("."), function(obj, f) {
if(obj) return obj[f];
}, data);
}
function setObjValue(field, data, value) {
var fieldArr = field.split('.');
return _.reduce(fieldArr, function(o, f, i) {
if(i == fieldArr.length-1) {
o[f] = value;
} else {
if(!o[f]) o[f] = {};
}
return o[f];
}, data);
}
implement as:
var util = require('./utility');
app.put('/record/:id', function(req, res) {
Record.findById(req.params.id, function(err, doc) {
if (!err) {
utils.updateDocument(doc, Record, req.params);
doc.save();
...
Maybe this has changed since this question was first asked, but you can update multiple paths in Mongoose with the set method ike:
// object
doc.set({
path : value,
path2 : {
path : value
}
});
doc.save();
References
http://mongoosejs.com/docs/api.html#document_Document-set
direct updating is not recommended according to this document:
http://mongoosejs.com/docs/2.7.x/docs/updating-documents.html
i solved it like this:
Book.findOne({isbn: req.params.isbn}, function (err, book){
if (err) {
res.send(422,'update failed');
} else {
//update fields
for (var field in Book.schema.paths) {
if ((field !== '_id') && (field !== '__v')) {
if (req.body[field] !== undefined) {
book[field] = req.body[field];
}
}
}
book.save();
}
});
If you want to update the entire document , you can delete the document based on its id and store the entire object again.
That object must contain data for each and every fields of the mongo document.
Here is an example.
mongoDBCollectionObject.findOneAndRemove({ // -- it will delete the entire document
_id: req.body.fieldsdata._id // here fiedsdata is exact copy with modification of previous data
}, function(err, data) {
var newFieldsData = new mongoDBCollectionObject(fieldsdata); //-- fieldsdata updated data
newFieldsData.save(function(err, data) { // save document to that collection with updated data
if (err) {
console.log(err);
} else
res.json({
success: true
});
});
})
To clarify the question, it looks like you are taking the Request parameters and using those to find and update the given document.
Is there any way of accomplishing this without having to write a Mongo query?
The obvious answer is to update the Model object with the value from the Request. Which is what you suggest...
Unless you want to assign individual properties and run save() at the end.
But it seems like you don't want to do this? It sounds like you want to update the Model object directly from the Request object?
You can do this if you really want. You just loop through req.params and set the doc values where appropriate.
for(var i in req.params) {
if(req.params[i] != doc[i]){
doc[i] = req.params[i];
}
}
It should be as simple as this. However, you only want to do this if you have a whole bunch of validation code on the Model objects. The whole point to the Model is that you don't want to get random data in the DB. The line above will generically "set" the correct values, but you'll definitely need to include code for authentication, authorization and validation around that simple for loop.
try to updating the collection without the find, like this
Record.update({_id:req.params.id}, {$set: { field: request.field }}, {upsert: true}, function(err{...})
The option upsert create the document if not exist.
In case you have a new object and want to update whole object in the database, you can update multiple fields at once like this:
find the object
get all schema paths (fields)
save the new object.
SomeModel.findOne({ 'id': 'yourid' },function (err, oldObject) {
if (err) return handleError(err);
// get all schema paths (fields)
SomeModel.schema.eachPath(function(path) {
// leave __id and __v alone
if (path != '_id' && path != '__v') {
// update the data from new object
oldObject[path] = newObject[path];
}
})
oldObject.save(function(err) {
if (err)
console.log(err)
});
})
A neat and clean approach would be using async await and findOneAndRemove along with create Here is the sample code
try {
let resp = await this.findOneAndRemove({ _id: req.body._id });
let entry = await this.create(req.body);
} catch (err) {
}
Don't Forget to mark this whole function as async

Resources