Saving JSON data correctly on nodeJS server - node.js

I'm still working on my SPA where can I track my expenses. Each Expense Item consists of a value, a date , description and tags.
The client send all these data to my server , where I want to save it in a JSON file.
My code looks like this right now:
(json.push isnt working)
client.on('message', function(value, date, descr, tags) {
console.log('message: ' + value, date, descr, tags );
var exp = new Object();
exp.id = id;
exp.value = value;
exp.date = date;
exp.tags = tags;
expArr[exp.id] = exp;
id++;
console.log(exp.id);
fs.readFile('expenses.json', function (err, data) {
var json = JSON.parse(data);
json.push(exp);
console.log(json);
fs.writeFile("expenses.json", JSON.stringify(exp),
function(err){
if (err) throw err;
console.log('The data was appended to file!');
});
})
});
My goal is, every newly added item should append to my JSON file.
In the end it should look like this for example:
{"expArray": [{"id":0,"value":"200","date":"dqwd","tags":"cnelw"},
{"id":1,"value":"300","date":"dqwd","tags":"ncjlekw"},
{"id":2,"value":"22","date":"dqwd","tags":"dnkqoe"}
]}
I dont know if it's necessary to to do an array there?
But I need to read the file again for the future and get the ID of the items to delete them on client-side or edit them.
Thanks for ur help!

try this:
client.on('message', function(value, date, descr, tags) {
console.log('message: ' + value, date, descr, tags );
// exp object
var exp = {id:id,value:value,date:date,tags:tags}
expArr[exp.id] = exp;
id++;
console.log(exp.id);
fs.readFile('expenses.json', function (err, data) {
var json = JSON.parse(data);
// _------- add expArray
json.expArray.push(exp);
console.log(json);
fs.writeFile("expenses.json", JSON.stringify(exp),
function(err){
if (err) throw err;
console.log('The data was appended to file!');
});
})
});

Related

Mongoose using $push on an array field overwrites last entry

I am trying to create an eventlog type of field on mongodb records where I can store a list of activity. The first time I run the function, it appends to the array correctly but subsequent calls overwrite the last entry instead of appending. If I restart the server or refresh the page in the browser, it will append once again then repeat the same behavior.
I'm learning node and javascript so I'm sure it's some mistake I've made but I don't seem able to figure it out.
Javascript on the client is a tabulator event.
cellEdited:function(cell){
//cell - cell component
const oldValue = cell.cell.oldValue;
const newValue = cell.cell.value;
const title = cell.cell.column.definition.title;
var report = cell.cell.row.data;
report.event = `Updated ${title} from ${oldValue} to ${newValue}`;
$.ajax({
type: 'POST',
url: '/api/update',
data: report,
dataType: 'json'
});
}
The route that its calling on the server:
app.post('/api/update', isAuthenticated, function(req, res) {
var report = req.body;
var reason = '';
if (typeof report.event !== 'undefined') {
reason = report.event;
delete report.event;
} else {
reason = 'Report updated';
}
db.DamageReport.findOneAndUpdate({ _id: report._id}, report, function (err, doc) {
if (err) {
console.log('Err updating report ', err);
return res.send(500, { error: err});
}
/*
* Write eventlog
*/
var event = {"date": new Date(), "user": req.user.email, "event": reason };
appendLog(doc._id, event);
return res.json(doc);
});
});
The appendLog function:
function appendLog(id, entry) {
/*
* entry format:
* date: Date
* user: String
* event: String
*/
if (typeof(entry.date) !== 'object') {
entry.date = new Date();
}
db.DamageReport.findByIdAndUpdate(id, {$push: {eventLog: entry}}, function(err, result) {
if (err) {
return console.log('Error writing eventLog: ', err);
}
return(result);
});
}
It wouldn't append more than one because the previous save contained the Eventlog array in it's original form so every time it saved, it set it back to the original array and then appended the last update.

Async is calling callback before last item?

I have an async.eachSeries() function which process a list of objects. After the list I want a res.send() function so I can send the result back to the frontend.
But I'm getting a
'Can't set headers after they are sent'
error on the res.send() line, which it's looks that the function is called before the list is completely processed.
module.exports.createOrder = function(req,res){
console.log("CreateOrder");
var orderDescription = "";
var payId = new ObjectId(); //create one id, to pay multiple orders at one time
var shopList = groupByShop(req.body.cart);
var orders = [];
var result = {};
console.log("before async");
//the cart is now sorted by shop, now we can make orders for each shop
async.eachSeries(Object.keys(shopList), function(key, callback){
console.log("in async");
var shop = shopList[key];
console.log("before saveOrder");
saveOrder(payId, shop, key, req.body, req.user, function(err, newOrder){
console.log("in saveorder");
if(err){
console.log("Err", err);
callback(err);
}else{
console.log("order saved");
orders.push(newOrder);
callback();
}
})
}, function(err){
if(err){
console.log("One or more orders are not saved:", err);
return res.status(400).json(err);
}else{
console.log("All orders are processed");
result = {
message: 'OK',
order: {
payId: orders[0].payId
}
};
return res.send(200).json(result);
}
})
}
What is going wrong here? Currently testing with one object in the 'shopList', and all log lines are visible in the server console.
When I remove the line, the function is working fine, but, of course, he is not sending any results. I also tried to move the line outside the function, but that cause, of course again, in a empty result{} and a sending before the function is done.
res.send(200) will send a HTML response with content '200' - what you want to do is res.status(200).json(result) although res.json(result) should also work fine.

How to send binary data in response from node.js server

I am new to mongodb.
I stored binary data with below code snippet:
var data = fs.readFileSync(path);
var image = new mongodb.Binary(data);
//open connection/collection
var record = {picname: id, content: image };
collection.save(record, {safe: true}, function(err,result){
if(err)
console.log(err.stack);
});//save
I can see the record size in db. there is binary data. record size also matched with file size. am happy.
Now, retrieved same binary data from mongo and trying to send it in response:
var record = {picname: id};
collection.findOne(record, function(err,result){
if(err)
console.log(err.stack);
else
{
console.log('before read from db for download.');
//HOW TO READ IMAGE/BINARY DATA FROM RESULT?
//I need to send result in response. Any Idea?
console.log('before read from db for download');
}
});
I am sending binary data with below code snippet. It's not working for all the files. What could be the issue:
collection.findOne(record, function(err,result){
if(err)
console.log(err.stack);
else
{
console.log('before read from db for download. result: [' + result.picname + " ], type: " + result.imagetype);
res.end(result.content.buffer, "binary");
console.log('Responded SUCCESS: ' + id );
}
});//findOne
Please let me know how to retrieve and send via response.
Thanks in advance
DD.
Your problem here is not so much with storing and reading the data, but is actually all about content types. So ideally you want to store this with your data as well as return the correct header information when you send the response.
So part of this would be mime type detection. There are modules available, mmmagic is one of them
var Magic = require('mmmagic').Magic;
var magic = new Magic();
var data = fs.readFileSync(path);
var image = new mongodb.Binary(data);
//open connection/collection
magic.detect(data,function(err,result) {
var record = {picname: id, content: image, mimeType: result };
collection.save(record, {safe: true}, function(err,result){
if(err)
console.log(err.stack);
});//save
});
Methods for writing the header vary, but with the base "http" for node you call as shown:
var record = {picname: id};
collection.findOne(record, function(err,result){
if(err)
console.log(err.stack);
else {
res.writeHead(200, {
'Content-Type': result.mimeType,
'Content-Length': result.content.length
});
res.write(result.content.buffer);
res.end();
}
});
So what effectively gets returned here is the binary data identified by it's correct mime type. So you can access this from an URL where you supply the means to lookup the document and view directly in a browser just as if it was a regular image file being served.

Node.js CSV module

I'm using a CSV module as in the example below.
I was wondering if there is a way to make sure that all the fields aren't inserted as strings, e.g. the number 1.23 is not inserted as string "1.23".
It seems to make everything type string.
var csv = require('csv');
var q = async.queue(myCollection.insert.bind(myCollection), 50);
csv()
.from.path(req.files.myCSV.path, {columns: true})
.transform(function(data, index, cb){
q.push(data, function (err, res) {
if (err) return cb(err);
cb(null, res[0]);
});
})
.on('end', function () {
q.drain = function() {};
})
In csv.transform(), before q.push(), you can convert fields using e.g. parseInt:
data.fieldX = parseInt(data.fieldX);
data.fieldY = parseFloat(data.fieldX);
You could also delete data.fieldY; or add fields data.fullName = data.first + ' ' + data.last;

Node.js Mongoose keeps adding same single element instead of all of them

In my code below, my value printed out at the console.log is correct, but when I search and go about entering the objects into the db, all the objects in the db contain the same hex, and image path but the id's are different. I tried first using findOne but the resulted in the same outcome. I am new to MongoDb so I am assuming it is just somethign I am doing stupid. Any ideas please send them my way :)
exports.addImage = function(req,res){
var params = req.body;
var colors = params.color;
var passedImg = params.path;
var ndxobj;
for(var index in colors){
ndxobj = colors[index];
//Values here are the correct index and contain valid data
console.log("col: ", ndxobj);
var query = clrModel.find({hex: ndxobj.hex}, function(err,result){
if(!err && result.length > 0){
console.log(result);
}else if(!err){
//We have an empty db for the searched obj
var locclr = new clrModel({
hex: ndxobj.hex
});
locclr.img.push({path:passedImg, date:ndxobj.imagedate});
locclr.save(function(error, data){
if(error){
console.log("Error in addImage find call: ",error);
res.json(error);
}
else{
console.log("Saving: ",data);
res.json(data);
}
});
}else {
//Handle error
}
});
}
};
I think that your paths are all the same because you set path to be passedImage, and passedImage is not updated from each index, but is set at the top of your code sample. As for the hex values being all the same, that seems to be happening because the callbacks are closing over ndxobj, so by the time they're called, all of them are looking at the same value. To make that work, you'll want to use a function to create your callbacks, something like what follows (hopefully I closed all my parens & brackets...). See this StackOverflow post for more info.
exports.addImage = function(req,res){
var makeCallback=function(ndxobj){
return function(err,result){
if(!err && result.length > 0){
console.log(result);
}else if(!err){
//We have an empty db for the searched obj
var locclr = new clrModel({
hex: ndxobj.hex
});
locclr.img.push({path:passedImg, date:ndxobj.imagedate});
locclr.save(function(error, data){
if(error){
console.log("Error in addImage find call: ",error);
res.json(error);
}else{
console.log("Saving: ",data);
res.json(data);
}
});
}else{
//Handle error
}
};
});
var params = req.body;
var colors = params.color;
var passedImg = params.path;
var ndxobj;
for(var index in colors){
ndxobj = colors[index];
//Values here are the correct index and contain valid data
console.log("col: ", ndxobj);
var query = clrModel.find({hex: ndxobj.hex}, makeCallback(ndxobj.hex));
}
};

Resources