Node express error: "can not set header after they are sent" - node.js

I define a express route like this:
qiniuRoute.js
module.exports = function (app) {
const qiniu = require('../controllers/qiniuController')
app.route('/upload')
.post(qiniu.uploadFile);
}
qiniuController.js
function uploadFile(req, res) {
const form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
console.log('receive');
console.log(files.file);
const localFile = files.file.path;
const config = new qiniu.conf.Config();
const formUploader = new qiniu.form_up.FormUploader(config);
const putExtra = new qiniu.form_up.PutExtra();
const key = files.file.name;
const uploadToken = renderUploadToken(req, res);
// fileupload
formUploader.putFile(uploadToken, key, localFile, putExtra, function (respErr,
respBody, respInfo) {
if (respErr) {
throw respErr;
res.json({
error: respErr.error
})
return;
}
console.log(respInfo)
if (respInfo.statusCode == 200) {
console.log(respBody);
res.json(respBody)
} else {
console.log(respInfo.statusCode);
console.log(respBody);
}
});
});
};
But when I make a file upload request error occurs show that "can not set header after they are sent", it seems that I response to client more than one time but I already return in error handle. I also have another guess that cause my fileupload is async function which means I can't res.json in my callback?

Related

How to store the readStream files of Node.js into Redis and also how to retrieve the stored readStream files from Redis?

I tried converting the readStream (Image) to string and then storing it in Redis. Then retrieving the string from Redis and converting it back to readStream. But it didn't work out.
function getFile(fileKey) {
console.log(fileKey);
const downloadParams = {
Key: fileKey,
Bucket: bucketName,
};
return s3.getObject(downloadParams).createReadStream();
}
exports.getFile = getFile;
For converting stream to string I'm using stream-to-string. It gets converted and stored in Redis.
const { getFile } = require("../s3");
const redis = require("redis");
const client = redis.createClient();
var toString = require("stream-to-string");
exports.getFileFromS3Controller = async (req, res) => {
console.log(req.params);
const path = req.params.path;
const key = req.params.key;
const readStream = getFile(path + "/" + key);
toString(readStream).then(function (msg) {
// Set data to Redis
client.setex(key, 3600, msg);
});
readStream.pipe(res);
};
On Retrieving from the Redis I am not getting it.
const redis = require("redis");
const client = redis.createClient(null, null, { detect_buffers: true });
const Readable = require("stream").Readable;
// Cache middleware
function cache(req, res, next) {
const { path, key } = req.params;
client.get(key, (err, data) => {
if (err) throw err;
if (data !== null) {
var s = new Readable();
s.push(data);
s.push(null);
s.pipe(res);
} else {
next();
}
});
}
router.get("/:path/:key", cache, getFileFromS3Controller);
You are not calling next. Another mistake is that the stream is not being saved anywhere in the req so you can access later from the controller. From what I see you are writing it directly in res which is a problem because after this you cannot use res anymore to send anything else.
Here it is the code (not tested)
exports.getFileFromS3Controller = (req, res) => {
if (req.fileStream) {
req.fileStream.pipe(res);
return
}
console.log(req.params);
const path = req.params.path;
const key = req.params.key;
const readStream = getFile(path + "/" + key);
toString(readStream).then(function (msg) {
// Set data to Redis
client.setex(key, 3600, msg);
// Conver string to readable
const readable = new Readable();
readable.push(msg);
readable.push(null);
readable.pipe(res);
});
};
function cache(req, res, next) {
const { path, key } = req.params;
client.get(key, (err, data) => {
if (err) throw err;
if (data !== null) {
var s = new Readable();
s.push(data);
s.push(null);
req.fileStream = s;
}
next();
});
}
Edit I fixed a mistake in my answer because a Readable stream cannot be rewinded.

node.js Why data from post requests that is saved in a json file resets to initial empty array after restart server?

I am working with express node.js, and I am trying to save datas from post request in a json file. but for some reason when I restart the server, my data from post request was supposed to save in roomDB.json file doesnt remain instead it resets to initial empty array...
Could anyone please advice? thank you very much.
here is my code
//saving function
const fs = require("fs");
exports.save =(data, PATH) =>{
return new Promise((resolve, reject) => {
fs.writeFile(PATH, JSON.stringify(data), function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
// code in router file to make requests
const express = require("express");
const router = express.Router();
const fs = require("fs");
const rooms = ("./roomDB.json");
const { addRoom} = require("./rooms");
router.get("/", (req, res)=>{
fs.readFile("roomDB.json", (err, data)=>{
if(err) return res.status(400);
res.send(roomDB_PATH)
})
});
router.get("/:id", (req, res)=>{
res.send("connect to a room");
});
router.post("/", (req, res)=>{
let roomName = req.body;
if(!roomName.name){
res.status(404);
res.end();
return;
}
let room =addRoom(roomName.name);
res.status(201).send(room)
})
module.exports = router;
*/
const uuid = require("uuid");
let roomdatas;
const {save} = require("./save");
const roomDB_PATH = "roomDB.json";
try {
roomdatas = JSON.parse(fs.readFileSync(roomDB_PATH));
} catch (e) {
roomdatas = []
save(roomdatas, roomDB_PATH);
}
const addRoom = (roomName) => {
roomName = roomName.trim().toLowerCase();
const existingRoom = roomdatas.find((room) => room.name === roomName);
if (existingRoom) {
return { error: 'chatroom has existed' };
}
let room = {
name: roomName,
id: uuid.v4(),
messages: [],
users: [],
created: +new Date()
};
roomdatas.push(room);
save(roomdatas, roomDB_PATH);
return { room };
};
module.exports ={addRoom};
I'm assuming that you are encountering an error with the JSON.parse(fs.readFileSync(roomDB_PATH)); call. This code runs every time your server is started (when you import the file into your router file), and if it encounters an error it is resetting the file to an empty array. Try logging the error to see what is causing it. You're currently completely suppressing the error with no way to tell why it is failing.

asynchronous code not working with event listener

*Edited for clarity and to reflect changes:
Hello. I'm struggling to understand why my async functions are working just fine individually, and when chained together, but not when chained together and fired off in an event listener....
worth noting: when i try to run this without an even listener, data passes from my app, through my post route, and to my endpoint the way it should, it is then returned to my app through the correct route, etc. However, i do get an error in the console that says :
error SyntaxError: Unexpected token < in JSON at position 0
however, when i try to run my chain of async functions on the click of a dom element i don't get the above error, data is passed to my post route, i can log the object that is posted:
Object: null prototype] { zipcode: '97206', feel: 'confused' }
but then my server doesn't save any data, and my get route is never triggered, and nothing gets sent back to my app.......
i'm fairly lost.....
full server and app code below:
server:
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use(express.static('public'));
//test values
let projectData = {
date: "today",
temp: "38",
feelings: "confused"
};
app.get("/", (req, res) => {
});
app.post("/postData", (req, res) => {
console.log(req.body)
projectData = {
date: req.body.date,
temp: req.body.temp,
feelings: req.body.feelings
}
console.log("post route was hit, saved:", projectData);
res.redirect("/")
});
app.get("/getData", (req, res) => {
console.log("getData route was hit, sent: ", projectData)
res.send(projectData);
})
app.listen(3000, (req, res) => {
console.log("Listening on port 3000");
});
app
let newDate = getDate();
const apiKey = "5fd7b3a253e67a551e88ff34a92b9e02";
const baseURL = "http://api.openweathermap.org/data/2.5/weather?";
// zip=${}&APPID=${}&units=metric;
const userData = {};
// returns the date //
function getDate() {
let d = new Date();
let newDate = d.getMonth() + "." + d.getDate() + "." + d.getFullYear();
return newDate;
}
//constructs string for api call, adds temp to userData object
const getData = async (apiUrl, zip, key) => {
const url = `${apiUrl}zip=${zip}&APPID=${key}&units=metric`;
const result = await fetch(url);
try {
let newData = await result.json()
// console.log(newData.main.temp)
userData.temp = newData.main.temp
}catch(error) {
console.log("error", error);
}
}
// getData(baseURL, 97206, apiKey)
// .then(result => {
// console.log(userData)
// })
//saves contents of userData Object to server
const postData = async (url, data) => {
const result = await fetch(url, {
method: "POST",
credentials: "same-origin",
headers: {
"Content-type": "application/json"
},
body: JSON.stringify(data)
});
try {
const newData = await result.json();
// console.log(newData);
return newData;
} catch (error) {
console.log("error", error);
}
};
// postData('/postData', userData);
//updates interface with projectData values from server
const updateUI = async url => {
const result = await fetch(url);
try {
const newData = await result.json();
document.getElementById("date").innerHTML = newData.date;
document.getElementById("temp").innerHTML = newData.temp;
document.getElementById("feelings").innerHTML = newData.feelings;
} catch (error) {
console.log("error", error);
}
};
// updateUI("/getData")
// THIS WORKS
userData.date = newDate;
userData.feelings = document.getElementById("feel").value;
const zipCode = document.getElementById("zipcode").value;
getData(baseURL, 97206, apiKey).then(result => {
postData("/postData", userData).then(result => {
updateUI("/getData");
});
});
// THIS DOESNT WORK
// document.getElementById("btn").addEventListener("click", e => {
// userData.date = newDate;
// userData.feelings = document.getElementById("feel").value;
// const zipCode = document.getElementById("zipcode").value;
// getData(baseURL, zipCode, apiKey).then(result => {
// postData("/postData", userData).then(result => {
// updateUI("/getData");
// });
// });
// });
EDIT:
I realized that the information that was being passed through the post route when my async functions are fired off by an event listener was actually just the form input element values, rather than the contents of the fetch/post request. after i removed the name attributes from the form inputs, i'm getting no data at all hitting my post route......yet the corosponding function works fine with not in the event listener.
I'm stumped.
well, the syntax error was solved by using .text() instead of .json() on the results of my post request.
adding e.preventDefault() as the first line of my event listener callback fixed the event listener, and now everything is working as it should.

expressjs returning controller object to res.send

I have a controller that makes an api call (few seconds delay) and then returns a JSON object that I want to send and appear on my view page. At the moment I am able to return the object and successfully load the route, but the object is not appearing on my browser page (status code: 200) and I'm not sure what I might be missing. Provided below is my route and controller.
Controller:
var express = require('express');
var apiRouter = express.Router();
var googleAnalytics = require('../google-analytics');
const { checkToken } = require("./components/token-validator");
apiRouter.use(checkToken);
apiRouter.get('/ga', function(req, res){
res.send(googleAnalytics())
});
module.exports = apiRouter;
Controller (googleAnalytics):
module.exports = () => {
console.log("google-analytics.js")
const {google} = require('googleapis');
const analyticsreporting = google.analyticsreporting('v4');
const view_id = '*view-id(';
... // resource: req information
analyticsreporting.reports.batchGet({
resource: req
}, (err, result) => {
if(err){
if(err.errors){
console.log(err.errors[0].message)
} else {
console.log(err)
}
} else {
return result.data;
}
//console.log("BREAK IN CONSOLE")
//console.log(err, result)
})
}
Object E.g.:
{"reports":[{"columnHeader":{"dimensions":["ga:sourceMedium"],"metricHeader":{"metricHeaderEntries":
...
]}}
You are not returning anything from the google-analytics.js. You need to make the function return a promise or use callback. analyticsreporting.reports.batchGet returns a promise so it is pretty easy to do that.
module.exports = () => {
console.log("google-analytics.js")
const {google} = require('googleapis');
const analyticsreporting = google.analyticsreporting('v4');
const view_id = '*view-id(';
... // resource: req information
return analyticsreporting.reports.batchGet({
resource: req
});
}
Now, in your /ga route, you can use async-await:
apiRouter.get('/ga', async function(req, res){
res.send(await googleAnalytics())
});

Mongodb not updating the data after put request returns successful

In trying to use the objectid in the mongoose schema as a reference to do a put to update a document in a collection. The console logs its as a success but when I refresh the page or look in the mongo shell nothing changes.
Heres the put in the expressjs router:
router.put('/messageupdate/:empId', function (req, res) {
var values = req.body;
console.log(values);
var empId = req.params.empId;
console.log(empId);
Message.update({empId: empId}, values, function(err, values) {
if (!err) {
res.json("okay");
} else {
res.write("fail");
}
});
})
Heres the service method:
updateServiceWithId(message: Message): Observable<any> {
console.log(message);
const body = JSON.stringify(message);
console.log(body);
const headers = new Headers({'Content-Type': 'application/json'});
return this.http.put('http://localhost:3000/messageupdate/:empId', body, {headers: headers});
}
Heres the client side method that triggers the put:
onUpdateMessage() {
var retVal = confirm("Do you want to continue ?");
if( retVal == true ){
const message = new Message(this.fname,this.lname,this.empId,this.number,this.occu);
console.log(this.fname);console.log(this.lname);
console.log(this.empId);console.log(this.occu);
this.messages.push(message);
this.messageService.updateServiceWithId(message)
.subscribe(
() => console.log('Success!'),
error => console.error(error)
);
}
else{
alert("Edit cancled!");
return false;
}
}

Resources