add new object into a array to a js file in nodeJS - javascript

I was just wondering how to add(push) data into an array via an express router endpoint. Suppose I have an array inside data/data.js directory and my router code look this:
const express = require('express');
const bodyParser = require('body-parser');
const productRouter = express.Router();
//! bring data
const { products } = require('../data/data');
productRouter.use(bodyParser.json());
productRouter
.route('/')
.post((req, res, next) => {
if (req.body !== null) {
//some logic
} else {
products.push(req.body);
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(products);
}
} else {
//handle error
}
});
module.exports = productRouter;
When I involve POST method in the endpoint of my route then it push the new data and response with an updated array. But when I check my express file then it still the old array. Why I lost the data?
I heartily thank if anyone helps me to figure out this.
As #Aditya toke and #Andre Nuechter suggested I update my code like this:
.post((req, res, next) => {
if (req.body !== null) {
if (products.some((product) => product.id === req.body.id)) {
err = new Error('Product with id:' + req.body.id + ' already exit');
err.status = 404;
return next(err);
} else {
const data_path = path.join(__dirname, '../data/data.js');
fs.appendFileSync(data_path, JSON.stringify(req.body));
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
res.json(products);
}
} else {
err = new Error('Body didnot contain product information');
err.status = 404;
return next(err);
}
});
But it pushes the new data like this:
exports.products = [
{
title: 'camera',
imageUrl: 'https://source.unsplash.com/gGgoDJRD2WM/300x300',
id: 1,
},
...
]
exports.orders = [];
{"title":"mens","imageUrl":"https://source.unsplash.com/1-nx1QR5dTE/300x300","id":"6"}
Which is not what I want. Is there any way to add this to the products array? Or any better approach?

As you mention about the saving data in the file,
that is only possible by using the filesystem to write data in the file,
I would recommend using the JSON extension file, as it's easy to parse and read.
You have to use the filesystem to write data in the file.
And to add further it can be utilised as a global variable all over the project.
There are multiple ways to play with the filesystem using node js.
https://nodejs.org/api/fs.html
Updated The Answer
#falamiw Follow these steps
1. Don't use data.js start using data.json
Structure inside data.json will be like this
{
products : [
{"title":"mens","imageUrl":"https://source.unsplash.com/1-nx1QR5dTE/300x300","id":"6"},
{"title":"women","imageUrl":"https://source.unsplash.com/1-nx1QR5dTE/300x300","id":"7"}
]
}
Code to make changes in this JSON file
const fs = require('fs');
let rawdata = fs.readFileSync('data.json');
let productArray = JSON.parse(rawdata);
// push changes to your array
productArray ['product'].push({ any thing you want to add })
After pushing object inside array now we will make changes in the data.json file using fileSystem.
let data = JSON.stringify(productArray );
fs.writeFileSync('data.json', data);
That's it now you can see the changes in the file.
I have not tested this code but I am sure it will work you just to debug and check its working.

The data is lost, because push does not alter the file on your disk.
To do that you need to use something like fs.writeFile.

Related

Issues On JSON File

const fs = require('fs');
const express = require('express');
const app = express();
app.use(express.json());
app.get('/submit', (req, res) => {
let Com_Title = req.query.ComTitle;
let Com_Text = req.query.ComText;
let data = {
Title: Com_Title,
Text: Com_Text,
}
console.log(data);
let jsonData = JSON.stringify(data);
// fs.writeFileSync('notes.json', dataJSON)
// let MyData = JSON.parse(jsonData);
fs.appendFileSync('ComplaintFile.json', jsonData, err => {
if (err) {
console.log(err);
res.sendStatus(404).end();
}
console.log('Data Added');
res.send('Added');
})
});
let port = 8080;
app.listen(port, () => {
console.log("Listening to 8080");
})
{
"Title": "Canteen Issues",
"Text": "A paragraph"
}{
"Title": "Canteen ",
"Text": "Topic sentences are similar "
}
I have issues on saving data in JSON. Actually is saving the data on JSON file but it have comma issues like if I add one or more data that next data storing without the comma.
Anyone faced this issue?
Using appendfilesync is not appropriate for storing JSON data like that because JSON is structured data and appendfilesync will just add data to the end of the file. You need to:
Read the data that is already in the file.
Parse the data as JSON.
Add the new data to the JSON object/array.
Stringify the JSON data.
Save the data to the file.
In this example the initial file would look like this:
[{"Title":"Title","Text":"Content"}]
So, it is an array. And you can then add objects to that array.
var fs = require("fs");
fs.readFile("data.json", function(err, buf) {
let dataStr = buf.toString();
let dataObj = JSON.parse(dataStr);
let newData = {Title: "Title", Text: "Content"};
dataObj.push(newData); // assuming that this is an array
let newDataStr = JSON.stringify(dataObj);
fs.writeFile("data.json", newDataStr, (err) => {
if (err) console.log(err);
console.log("Successfully Written to File.");
});
});
You are adding data correctly, you just need to get data from file if exist then append new data with previous data.

Access express response data client side

In my express router I check if the data inserted on a form are valid then if they are I render another page passing form data. I would like to access the data I pass client-side. On the chat.ejs view I have a chatroom.js client file, I want to access the data there without having to access them in a script tag.
I thought about using Ajax but the only answer I found here on StackOverflow was marked as wrong, so how do I go about that?
router.js
module.exports=function(app) {
const express = require('express');
const router = express.Router();
const {check, validationResult} = require('express-validator');
const {matchedData} = require('express-validator/filter');
router.get('/', (req, res) => {
res.render('index', {
data: {},
errors: {}
})
});
router.post('/enter', [
check('username')
.isLength({min: 1})
.withMessage('Username is required').trim(),
check('room')//implement personalized check
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.render('index', {
data: req.body,
errors: errors.mapped()
})
}
else {
const data = matchedData(req);
return res.render('chat',{
user: data.username,
room:data.room
})
}
});
return router;
//MOVE TO SUPPORT
function find(name) {
return 1;
}
}
there is really nothing client-side so far so It seems useless just posting my views. Alternatively, I could use Ajax on client.ejs to handle the form submission but I would like to keep this clean and handle the routing with the router file.
I ended up creating two global variables in a script tag for my index.ejs page like this
<script>
var user = <%- JSON.stringify( user ) %>
var room = <%- JSON.stringify(room)%>;
</script>
and then I could access them in the chatroom.js file linked below

How can I pass options into an imported module?

I have a utility module that creates an instance of a multer-gridfs storage engine for uploading files to my Mongo database. I use this module inside of any API route that requires the need to upload files.
I need to be able to update the metadata property value with a unique identifier. More than likely this will be the mongoose _id of the user uploading the file, but for now I am not concerned with that aspect of it. I really just want to know if I can change the metadata property dynamically.
Here is the storage engine gridFs_upload_engine.js:
const mongoose = require('mongoose');
const path = require('path');
const crypto = require('crypto');
const multer = require('multer');
const GridFsStorage = require('multer-gridfs-storage');
const Grid = require('gridfs-stream');
//Init Upload Engine
let gfs;
//Global instance of the DB connection
const database = mongoose.connection;
const mongoDb = process.env.MONGODB_URI || process.env.MLAB_URL;
database.once('open', () => {
//Init Stream
gfs = Grid(database.db, mongoose.mongo);
gfs.collection('uploads');
});
//Create Storage Engine
const storage = new GridFsStorage({
url: mongoDb,
file: (res, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: 'uploads',
metadata: 'NEED TO UPDATE THIS'
};
resolve(fileInfo);
});
});
}
});
const uploadEngine = multer({ storage });
module.exports = {
uploadEngine,
gfs
};
Above you can see the metadata property that I need to be able to dynamically change with some undetermined unique identifier. Is it possible to do that with an exported file?
Here is how I am utilizing it inside of an API route:
const express = require('express');
const router = express.Router();
//Controllers
const upload_controller = require('../../controllers/uploader');
//Utilities
const upload = require('../../utils/gridFs_upload_engine');
const { uploadEngine } = upload;
//Upload Single File
router.post(
'/single',
uploadEngine.single('file'),
upload_controller.upload_single_file
);
//Upload Multiple Files
//Max file uploads at once set to 30
router.post(
'/multiple',
uploadEngine.array('file', 30),
upload_controller.upload_multiple_files
);
module.exports = router;
I pass the uploadEngine into the API route here, so that the route controller can use it, and that works with no issue. I am just having quite a time trying to figure out how to update metatdata dynamically and I am leaning towards my current implementation not allowing for that.
I don't know much about node and have now idea what multer-gridfs is but I can answer How can I pass options into an imported module?
You can export an function that returns another function. And you would import it like
const configFunction = require('nameoffile')
// this returns a functions with the config you want
const doSomethingDependingOnTheConfig = configFunction({...someConfig})
And in the file you are importing you would have a function returning another function like
const configFunction = ({...someConfig}) => (your, func) => {
// do what you want deppending on the config
}
module.exports = configFunction
I know this doesn't answer your question the way you want, but answer you question title and I hope this give you a better understanding of how to do what you want to do.
If this doesn't help, just let me know.
You would need to pass a parameter to the module gridFs_upload_engine.js and do the magic there.
An example could be:
In gridFs_upload_engine.js file:
function uploadEngine (id, file) {
// update what you want
}
module.exports = {
...
uploadEngine: uploadEngine
}
In your router:
const upload = require('../../utils/gridFs_upload_engine')
...
router.post('/single/:id', function(req, res, next) {
...
upload.uploadEngine(req.params.id, file)
next()
}, upload_controller.upload_single_file)
In other words, when you are exposing gfs and uploadEngine inside your module, you could instead expose a function that would receive the arguments needed to perform the upload.

Writing web scraped data to JSON file using NodeJS

I'm writing an application that scrapes fan sites for characters as a practice exercise. Currently I have an array of URLs that I am looping through and scraping the data I want, then outputting this data to a output.json file to store for later. I am having issues with my formatting when writing to this file.
Maybe I should store my data differently, I am open to suggestions on best practices/other methods. I would just like this data accessible later.
server.js
var express = require('express');
var cheerio = require('cheerio');
var app = express();
var rp = require('request-promise');
var fsp = require('fs-promise');
app.get('/', function(req, res){
urls = [
'fansite.com/boss1', 'fansite.com/boss2'
];
function parse(html) {
var bosses = require('./output.json');
var $ = cheerio.load(html);
$('.page-header__title').filter(function () {
var data = $(this);
name = data.text();
bosses.name = name;
})
return bosses;
}
var append = file => content => fsp.appendFile(file, JSON.stringify(content, null, 2));
urls.forEach(function (url) {
rp(url)
.then(parse)
.then(append('output.json'))
.then(() => console.log('Success'))
.then(res.send('Bosses Updated.'))
.catch(err => console.log('Error:', err));
});
})
app.listen('8081')
console.log('Running on port 8081');
exports = module.exports = app;
output.json
{
}{
"name": "Boss1"
}{
"name": "Boss2"
}
You're better off just modifying the in-memory javascript object, and then saving it all to the file in an overwrite / replace kind of approach, rather than appending to the file (unless you expect the file to become so huge that it breaks memory limits).
To do that, just maintain an in-memory copy of the data and then just write it out: fs.writeFile(fileName, JSON.stringify(content, null, 4));
Otherwise, you have to figure out how to insert the new object inside the old one, or risk making it invalid json.

Express over Node.js - TypeError: Cannot read property 'forEach' of undefined

I'm building my first node/express app and am following this tut.
I am at a point where I am trying to get all JSON data and put it in an array to be sent to the template and rendered. When I try to run the app via CLI, I get the following error:
Directory Structure
The data output at the var blogsurlall location
hellotest.js
var routes = require('./routes/index');
var express = require('express');
var app = express();
var request = require("request");
var blogsurlall = "https://[JSON export URL location configured in a Drupal 8 view]";
app.set('view engine','ejs');
var server = app.listen (2000, function(){ console.log('Waiting for you on port 2000'); });
/* Get all global blogs data */
request({
url: blogsurlall,
json: true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
blogsdata_all = body;
}
// Create blogs array for footer.
var blogs = [];
// Fill up the array with blogs.
blogsdata_all.blogs.forEach(function(item){
blogs = blogs.concat(item);
});
app.locals.blogsdata = blogs;
});
app.use('/', routes);
index.js
var express = require('express');
var routes = express.Router();
routes.get('/', function(req, res){ res.render('default',{title: 'Home', body: 'blogsdata'}); });
routes.get('/about-us', function(req, res){ res.send('<h1>Lucius Websystems</h1>Amsterdam, The Netherlands'); });
routes.get('/about/:name?', function(req, res){ var name = req.params.name; res.send('<h1>' +name +'</h1>About text'); });
/* GET Blog detail page. */
routes.get('/blog/:blogid', function(req, res, next) {
// Place json data in a var.
var blogsdata = req.app.locals.blogsdata;
// Create array.
var blogItem = [];
// Check and build current URL
var currentURL = '/blog/' + req.params.blogid;
// Lop through json data and pick correct blog-item based on current URL.
blogsdata.forEach(function (item) {
if (item.title == currentURL) {
blogItem = item;
}
});
if (blogItem.length == 0) {
// Render the 404 page.
res.render('404', {
title: '404',
body: '404'
});
} else {
// Render the blog page.
res.render('blog-detail', {
blog: blogItem
});
}
});
module.exports = routes;
From the CLI error, it appears no blog data is even returned to be read into the array.
I have carefully gone through the tutorial several times and I think there are steps that may be implied that I am missing.
Can someone please help me understand how to get the blog data so that it can be read into the array and output to my template?
Also open to troubleshooting suggestions in comments.
Thanks for reading!
The error is raising in this line:
blogsdata_all.blogs.forEach(function(item){
As the error says, blogs is undefined.
If there is an error in the request or status code isn't 200, the body is not assigned to the variable, but you are not finishing the execution, so the variable in that case would be undefined.
Other possible problem is the json received doesn't have blogs as key of the body.
Check this both things and let us know if you found the problem

Categories

Resources