where to should write res.send() in node.js app - javascript

In my app I have a category collection that saves the category title with its image ID. The image is saved in another collection with its meta data like path , type and so on. So for retrieving category I should retrieve category image in image collection by its ID and add image path to category object that is retrieved from category collection and send it to client...But I don't know where should I send categories to client?
When I send the response face this error :
throw er; // Unhandled 'error' event
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:561:11)
at ServerResponse.header (H:\node.js\online-store\app\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (H:\node.js\online-store\app\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (H:\node.js\online-store\app\node_modules\express\lib\response.js:267:15)
at ServerResponse.send (H:\node.js\online-store\app\node_modules\express\lib\response.js:158:21)
at H:\node.js\online-store\app\controllers\pcategory.controller.js:123:19
at H:\node.js\online-store\app\node_modules\mongoose\lib\model.js:4845:18
at processTicksAndRejections (internal/process/task_queues.js:77:11)
Emitted 'error' event on Function instance at:
at H:\node.js\online-store\app\node_modules\mongoose\lib\model.js:4847:15
at processTicksAndRejections (internal/process/task_queues.js:77:11) {
code: 'ERR_HTTP_HEADERS_SENT'
}
This is my code :
exports.getAll = async (req, res) => {
try{
const categories = await ProductCategory.find({});
categories.map(async(category)=>{
await File.findById(category.imageID).exec(function(err,file){
if(err){
console.log(err)
}else if(file) {
category.imagePath = file.file_path;
tempCategories.push(category)
}
res.send(tempCategories);
})
})
return res.send(tempCategories);
}catch {
res.json(err =>{
console.log(err);
res.status(500).send({
message:
err.message || "There is an error in retrieving category"
});
})
}
}

The problem is that nothing in your code is waiting for the asynchronous operations you're doing in your map callback to complete, so it does the res.send at the end right away — and then does res.send again within the map callback later when the async operations complete. Instead, wait for them to finish and send the result.
Also, you're using res.send where I suspect you want res.json, and using res.json later incorrectly (it doesn't take a callback).
See comments:
exports.getAll = async (req, res) => {
try {
// Get the categories
const categories = await ProductCategory.find({});
// Get the files for the categories, wait for the result
const result = await Promise.all(categories.map(async (category) => {
const file = await File.findById(category.imageID).exec();
// You probably can't modify the `category` object, so let's create
// and return a new object
return {...category, imagePath: file.file_path};
}));
// Send the result converted to JSON
return res.json(tempCategories);
} catch (err) { // Accept the error
// Send an error response
res.status(500).json({
message: err.message || "There is an error in retrieving category"
});
}
};
Side note: Your original code was using map without using the array it creates. That's an antipattern (sadly it seems to be one someone somewhere is teaching). I wrote up why and what to do instead here. (In my update to your code, I still use map, but I use the array it creates, passing it to Promise.all so we can wait for all those promises to settle.)

Your Code Like this,
Now Issue is You are sending two times Headers.
You can use like this, Firstly Declare array and push into it what you need and then last of your logic return it or send it.
exports.getAll = async (req, res) => {
try {
const categories = await ProductCategory.find({});
let tempCategories = []; // New Line
await Promise.all(categories.map(async (category) => {
await File.findById(category.imageID).exec(function (err, file) {
if (err) {
console.log(err)
} else if (file) {
category.imagePath = file.file_path;
tempCategories.push(category)
}
});
return category;
}));
res.send(tempCategories);
} catch {
res.json(err => {
console.log(err);
res.status(500).send({
message:
err.message || "There is an error in retrieving category"
});
})
}
}

Related

Res.send sends an empty object, even though console.log shows it isn't empty?

I am trying to use the google-sheets api with express and don't have much experience with javascript. I'm attempting to use pass a json object from express to react, but it seems that whenever I finally send the object, it just renders as empty on the frontend?
I've tried using res.body/res.data, but the object doesn't seem to have either. I've also tried to put as many awaits as I can everywhere to make sure the object is loaded in before sending, but nothing seems to do the trick. If I use res.json or res.send with just the response object, I get a circular structure converting to JSON error. Here is the code I'm working with.
async function docShit() {
// Initialize the sheet - doc ID is the long id in the sheets URL
const doc = new GoogleSpreadsheet(
"--SPREADSHEET ID--"
);
// Initialize Auth - see https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication
await doc.useServiceAccountAuth({
// env var values are copied from service account credentials generated by google
// see "Authentication" section in docs for more info
client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
private_key: process.env.GOOGLE_PRIVATE_KEY,
});
await doc.loadInfo(); // loads document properties and worksheets
const sheet = doc.sheetsByTitle[--WORKSHEET TITLE--];
const rows = await sheet.getRows(); // can pass in { limit, offset }
return rows;
}
app.get("/home", async (req, res) => {
try {
await docShit()
.then((response) => {
res.send(Promise.resolve(response)); //console log shows the object, but res.send just sends nothing??
})
.catch((err) => console.log(err));
} catch (err) {
console.error(err.message);
}
});
There is no res.send at all in your code. Also, you use await and .then together, but I consider them alternatives. Try the following:
app.get("/home", async (req, res, next) => {
try {
var response = await docShit();
console.log(response);
/* If response is circular, decide which parts of it you want to send.
The following is just an example. */
res.json(response.map(function(row) {
return {id: row.id, cells: row.cells.map(function(cell) {
return {id: cell.id, value: cell.value};
};
})};
} catch (err) {
console.error(err.message);
next(err);
}
});

In nodejs Rest api call, puppeteer page evaluate not returning the response and throws the error

In nodejs Rest api call, puppeteer page evaluate not returning the response and throws the error.
How to return object after executing all the steps, without async
app.get("/api/register", function (req, res) {
res = page.evaluate((res) => {
webex.meetings
.register()
.then(() => {
console.log("Authentication#register() :: successfully registered");
return res.status(200).json({ message: "Successfully Registered" });
})
.catch((error) => {
console.error( "Authentication#register() :: error registering", error);
return res.status(400).json({ message: "Successfully Registered" });
})
}, res);
});
error
:\Users\sansubbu\git\webRTC\node_modules\puppeteer-core\lib\cjs\puppeteer\common\Connection.js:115
const stringifiedMessage = JSON.stringify(Object.assign({}, message, { id }));
^
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Socket'
| property 'parser' -> object with constructor 'HTTPParser'
--- property 'socket' closes the circle Recursive objects are not allowed.
at JSON.stringify ()
at Connection._rawSend (C:\Users\sansubbu\git\webRTC\node_modules\puppeteer-core\lib\cjs\puppeteer\common\Connection.js:115:41)
at CDPSessionImpl.send (C:\Users\sansubbu\git\webRTC\node_modules\puppeteer-core\lib\cjs\puppeteer\common\Connection.js:320:82)
at ExecutionContext._ExecutionContext_evaluate (C:\Users\sansubbu\git\webRTC\node_modules\puppeteer-core\lib\cjs\puppeteer\common\ExecutionContext.js:211:46)
res is a complex, circular structure that only works in the Node environment. Even if you could, passing it to the browser console via page.evaluate() would take it out of Node, where it belongs, leaving it in an environment where it doesn't make any sense (browsers can't respond to requests as if they were a server).
Instead, try returning a boolean and branching on that on the Node side, where req/res are in their natural environment:
app.get("/api/register", async (req, res) => {
const success = await page.evaluate(async () => {
try {
await webex.meetings.register();
return true;
}
catch (err) {
return false;
}
});
if (success) {
console.log("Authentication#register() :: successfully registered");
return res.status(200).json({message: "Successfully Registered"});
}
console.error("Authentication#register() :: error registering", error);
// probably not the message you want but left as-is...
return res.status(400).json({message: "Successfully Registered"});
});
This is untested since you haven't provided a complete, reproducible example.
page.exposeFunction is another possible tool for triggering Node code based on a condition in the browser, but that seems like overkill here.
Finally, I'm not sure what page is, but typically you need a different page for each request. See this answer for suggested Express + Puppeteer boilerplate.

Firebase Admin update function calling multiple callbacks in nodejs

I know for a fact that reading data from firebase with firebase admin returns multiple callbacks. that is why I use ref.once(), like example below:
const ref = db.ref('clients');
ref.once('value', (snapshot) => {
res.send(snapshot.val());
}, (errorObject) => {
console.log('The read failed: ' + errorObject.name);
});
But, when I try to update data I get into the same trouble of receiving multiple callbacks crashing my application, but I can't use once in ref.update, what can I do to prevent receiving multiple callbacks?
app.get('/set-client', (req, res) => {
const ref = db.ref(`users/new_users`)
ref.update({ 'client': uid_client}).then(function(){
console.log("Data saved successfully.");
res.status(200).send("successful")
}).catch(function(error) {
console.log("Data could not be saved." + error);
res.status(201).send("failed")
});
});
Here is a code example.
When interacting with responses in the way you've shown, you might find that using the .then(onFulfilled, onRejected) variant of the then() method may be of use.
Quick note: When using this approach, care must be taken to understand that if the onFulfilled handler throws an exception/rejects, the sibling onRejected handler will not be called with its exception. The exception is instead passed onto the next chained onRejected handler in later then/catch calls. The sibling will only catch exceptions/rejections from steps prior to it in the chain.
Here is an example of the difference:
const somePromise = /* ... */;
const resultPromise = somePromise
.then((data) => { /* handle data */ }) // an error thrown here
.catch((err) => { /* handle error */ }) // gets caught here
// vs.
const resultPromise = somePromise
.then(
(data) => { /* handle data */ }, // an error thrown here, will reject resultPromise
(err) => { /* handle error */ } // instead of being handled here
)
This trait of the onRejected handler in .then(onFulfilled, onRejected) can be applied in a way where headers can't be sent twice for the same response. If for whatever reason the onFulfilled handler throws an exception while trying to send a response, the onRejected handler that is also responsible for sending a response is skipped - preventing any headers already sent errors.
This means that the first code block gets swapped out for:
const ref = db.ref('clients');
ref.once('value')
.then(
(snapshot) => { // got data successfully
console.log('got data successfully');
// don't forget to check snapshot.exists() if data could be missing
res.send(snapshot.val()); // using .json() over .send() is recommended for arbitrary data
},
(error) => { // failed to get data/permission
console.error('Failed to read data at /clients: ', error);
res.status(500).send('Data unavailable.');
}
)
.catch(
(error) => { // if here, either of the above blocks failed - probably due to an error related to the response.
console.error('Failed to send response to client: ', error);
try { res.end() } catch (e) {} // forcefully terminate connection if not already
}
);
and the second code block for:
app.get('/set-client', (req, res) => {
const ref = db.ref(`users/new_users`)
ref.update({ 'client': uid_client }) // uid_client is undefined?
.then(
() => {
console.log("Data updated successfully.");
res.status(200).send("successful");
},
(error) => {
console.error("Data could not be saved.", error);
res.status(500).send("failed"); // don't use HTTP 201 Created here
}
)
.catch(
(error) => { // if here, either of the above blocks failed - probably due to an error related to the response.
console.error('Failed to send response to client: ', error);
try { res.end() } catch (e) {} // forcefully terminate connection if not already
}
);
});
The error handler that logs response errors could be rewritten so that it can be reused by taking in the relevant response object (so it can terminated when needed) and returning the error handler:
const buildResponseErrorHandler = (response) => ((error) => {
console.error('Failed to send response to client: ', error);
try { response.end() } catch (e) {} // forcefully terminate connection if not already
});
// usage:
somePromise
.then(
sendResponseHandlerForSuccess,
sendResponseHandlerForFailure
)
.catch(buildResponseErrorHandler(res)) // buildResponseErrorHandler(res) returns (err) => { /* logs problem */ }

Database Querying function not running asynchronously

So I created a function in my node server which takes in a query string, runs it on my db, and returns the results. I then wanted to use my function asynchronously using async await throughout my routes instead of having nested query within, nested query, within nested query etc.
So here is the code:
const runQuery = queryString => {
console.log("...runQuery")
db.query(queryString, (error, results, fields) => {
if (error) {
console.log("runQuery: FAILURE");
return error;
}
else {
console.log("runQuery: SUCCESS");
return(results);
}
})
}
register.post("/", async (req, res) => {
console.log(req.body);
const results = await runQuery("select * from test1");
res.send(results);
})
The database should have 3 entries, but unfortunately, it returns nothing. Meaning results is an empty variable when it is sent, meaning JS never properly waits for it to capture the db results. How can I use my function asynchronously, and how is this even feasible?
It seems your function "runQuery" does not return a promise, in fact, it's not returning anything. You are using "return" in the callback of the db.query function, not the function "runQuery" itself.
Since runQuery is performing an asynchronous operation, the result ought to be resolved via a promise (which is what the "await" in your request handler is looking for).
I'm not exactly sure but it seems you are using MySql, and I could not find anything on the npm page of the mysql package regarding the query being promisified, so we'll promisify it ourselves:
const runQuery = (queryString) => new Promise((resolve,reject) => {
console.log("...runQuery")
db.query(queryString, (error, results, fields) => {
if (error) {
console.error("runQuery: FAILURE");
reject(error);
} else {
console.log("runQuery: SUCCESS");
resolve(results);
}
})
})
register.post("/", async (req, res) => {
console.log(req.body);
try{
const results = await runQuery("select * from test1");
res.send(results);
}catch(e){
console.error(`ERROR THROWN BY runQuery:`,e);
res.status(500).send({
message: e.message || "INTERNAL SERVER ERROR"
})
}
})
Note that if an error occurs, our promisified function will reject the error and it will NOT be stored in the "results" variable in our request handler. Instead, an error will be thrown which needs to be handled. Therefore, it is always a good practice to put any async/await calls inside a try/catch.

How can I get Express to realize there was an error with my Teradata database call?

Currently I have Express running a call against my Teradata database and it acts/performs perfectly when everything works.
However if my Teradata call returns an error I see an output in my console window but I cannot setup an error handler.
I realize this should be extremely basic but I am very new to Express. Any help would be appreciated.
Express Call code:
router.post('/sp_run', function (req, res) {
var sql = "CALL DB.STORED_PROC1(1,P_ERROR_CODE,P_MSG);";
console.log(sql);
return teradata.read(sql)
.then((x) => {
console.log(x);
res.send(x);
});
});
Error info I see in my console.
CALL DB.STORED_PROC1(1,P_ERROR_CODE,P_MSG);
express_1 | 2019-6-27 21:07:10 - error: [Teradata] Unable to execute query: CALL DB.STORED_PROC1(1,P_ERROR_CODE,P_MSG);
express_1 | Unhandled rejection Error: Error running instance method
express_1 | java.sql.SQLException: [Teradata Database] [TeraJDBC 16.20.00.10] [Error 7627] [SQLState HY000] STORED_PROC1:SELECT-INTO returned more than one row.
End game result -- if an error occurs I want to send an email and to send a response back so my front-end isn't just waiting for ever.
We can't send promise from here so just send response with status variable with type boolean
then apply condition on frontend
router.post('/sp_run', function (req, res) {
var sql = "CALL DB.STORED_PROC1(1,P_ERROR_CODE,P_MSG);";
console.log(sql);
return teradata.read(sql)
.then((x) => {
return res.json({
status: true,
message: 'All work fine',
result: x
})
}).catch(error => {
//send email about failure
return res.json({
status: false,
message: 'Failed',
error: error.message
})
});
});
Or you can send res with status code and handle using try/catch
On front end
const getData = () => {
// Here axios is use for call a api you can use fetch or anthing.
axios.post('/sp_run', {}).then(data => {
if(!data.status) {
alert(data.error)
} else {
// Play with data
}
})
}

Categories