Searching one collection multiple times in one call - node.js

I have an array with multiple values which need to be used as queries to search a collection. I am not sure of a way of doing this using one route. for example:
router.get('/', ensureAuthenticated, (req, res) => {
let ArrayOfIds = [id1, id2, id3]
Movie.find({user: req.user.id}).then(items => {
//Something along this lines
items.subscriptions.forEach(subscription => {
Movie.find({user: subscription})
//This wont work beacuse of the callback promise
.then(movies => {
items.push(movies)
})
})
res.render('index/home',
{pageName: 'Movies', items: items})
.catch(err => console.log(err));
});
})
I want to search the same collection (Movie) for each id and add it to the items object. Doing it with a for loop seems to create a set header error. Any ideas?

This S/O response should have your answer...
Error: Can't set headers after they are sent to the client
From the response:
The error "Error: Can't set headers after they are sent." means that
you're already in the Body or Finished state, but some function tried
to set a header or statusCode. When you see this error, try to look
for anything that tries to send a header after some of the body has
already been written. For example, look for callbacks that are
accidentally called twice, or any error that happens after the body is
sent.
In your case, you called res.redirect(), which caused the response to
become Finished. Then your code threw an error (res.req is null). and
since the error happened within your actual function(req, res, next)
(not within a callback), Connect was able to catch it and then tried
to send a 500 error page. But since the headers were already sent,
Node.js's setHeader threw the error that you saw.
It's likely that you're calling res.redirect() multiple times if you're implementing a for-loop.

Maybe if I understand correctly, you can query the collection with list of ids in your case ArrayOfIds into in query of mongo.
Check $in query of mongoDB.
FYI: create some index on that field.

Related

NodejS : Error: Can't set headers after they are sent to the client

I am quite new to node and am currently encountering an error that seems pretty common: Error: Can't set headers after they are sent to the client.
I've read a few threads but cannot wrap my head around this one, here is my code so far:
router.get('/:id', (req, res) => User.findAll({
where : {
id: req.params.id,
},
attributes : {
exclude : ['updatedAt'],
}
})
.then(user => {
res.send(user);
res.sendStatus(200);
})
.catch((err) => console.log(err)));
What's wrong here? Any help and advices would be greatly appreciated!
The real reason behind this problem is that you are using both
res.send(user) and res.sendStatus(200).
The error says: Error: Can't set headers after they are sent to the client
You already send something to the client (in this case res.send(user)) and you can't send another thing now (in this case res.sendStatus(200)).
Once you do res.send, res.redirect, res.sendStatus, res.render, you should not do add any of these again.
You will get this problem if you are using more than one at the same time. Even if you have something like:
for(condition) {
res.send("1st");
}
res.send("2nd");
You should add return statements in front of them like:
for(condition) {
return res.send("1st");
}
return res.send("2nd");
Also as #Halil Akar said that 200 status is always returned with res.send. You can also use res.status(301).send(user) to send data and a status at the same time.
I hope it helps.

Handling request simltaneously in nodejs and passing it's response using only one res.send

I am developing an API which takes input in XML containing IDs for media and gives output in XMLform with details of given IDs. I am facing a problem while sending the response of second simultaneous request; here the second request goes into loop showing "loading" on postman.
What I am doing is calling a function in app.post which parses the media and gives output in the callback and send it using res.send, but it works only for single request.
While doing parallel request to same API either it goes in loop or it gives can't set the headers after they are sent as I am using res.send but res.send is the only way which I can use to send the response (even the next doesn't work).
var getCompositeData = function(req, res, next){
abc.getData(req.body, function(err, xmlOutput){
if(err){
console.log("error");
} else {
xmlData = xmlOutput
return next()
}
}
app.post(apiUrl, [
rawBodyParser({
type: 'application/xml'
}),
app.oauth.authorise()
], getCompositeData, function (req, res) {
res.setHeader('Content-Type', 'application/xml');
res.send(xmlData);
});
There are several issues with your code:
if (err) {
console.log("error");
}
If an error occurs, you still need to make sure a response will be sent back, otherwise the request will stall until a timeout happens. You can pass an error to next, and Express will handle it:
if (err) {
return next(err);
}
Next problem:
xmlData = xmlOutput
xmlData is an undeclared variable, which gets overwritten with each request. If two requests happens at (almost) the same time, it's likely that one client gets back an incorrect response (remember, Node.js runs JS code in a single thread; there is not thread-local storage so xmlData gets shared between all requests).
A good place to "store" this sort of data is in res.locals:
res.locals.xmlData = xmlOutput;
return next();
// and later:
res.send(res.locals.xmlData);

Hows should I structure middleware to verify an ID exists in an external service in a Node/Express app?

I've currently written middleware to verify an ID exists in an external services (Salesforce). I initially wrote it when it was a single use app, but now I'm trying to make it work with different routes, so I want it to be fairly generic.
I don't even know if middleware is the right way to go, or if I should just call the function before saving the specific form.
I've got a form where someone puts in some information about a project, and the salesforce ID. For background, the salesforce ID is actually an auto-increment number, and I need to convert that to the actual salesforce system ID before I use jsForce to create a new object linked to that ID.
My route looks like this:
router.post('/invoice/add', ensureLoggedIn, invoiceController.validateInvoice, catchErrors(sfdc.validateSFID), catchErrors(invoiceController.saveInvoice))
So, I've got a middleware that does this:
exports.validateSFID = async(req, res, next) => {
const salesforceProjectNumber = req.body.SF_Opportunity
const sfResult = await conn.search(`FIND ... long query`, (err, result) => {
if (err || result.searchRecords.length !== 1) {
req.flash('error', 'Unable to find a Salesforce Job with that ID number.')
console.error(`ERROR: ${req.user.displayName} errored when looking up job number ${salesforceProjectNumber}.`)
return result
}
})
if (sfResult.searchRecords.length > 0) {
req.body.salesforce_Opportunity_id = sfResult.searchRecords[0].Id //Create a generic variable to hold the salesforce opportunity so it works regardless of the custom object name
res.locals.Opportunity_Clean_Name = sfResult.searchRecords[0].Name
}
next()
}
The query rarely throws an error, but in this case, an error is basically returning !1 records.
When that happens, I want to flash a message on the screen saying the ID wasn't found, but keep the form filled in.
When an ID is found, I want to proceed to save it and NOT display the form fields anymore.
This middleware needs to work regardless of the form I'm using, I want to be able to pipe in the middleware from any form that might require a user to enter a salesforce job as a field.
Any thoughts on how best to handle it all?
You can use your middleware by using app.use() function
app.use((req, res, next) => {
// Every time a request has made, this middleware will fire
console.log('Howdy');
})

How to get Express to return the object I just deleted in MongoDB

Feel free to let me know if this isn't a common practice - I'm a fairly new programmer - but I thought I've seen APIs in the past that, when you submit a DELETE request to a resource (/todo/1234), some servers will return the object you just deleted in the response. Is that a thing? If so, I'd be interested in learning how to do it. Here's what I have:
.delete(function (req, res) {
Todo.findById(req.params.todoId).remove(function (err) {
if (err) res.status(500).send(err);
res.send("Todo item successfully deleted");
});
});
This code does delete the item, but I would like to return the item that got deleted in the response instead of a string message. If that's a normal/okay thing to do. If it isn't normal or okay for some reason, please let me know why and I'll just move on. Or perhaps there's a more common way.
This is what I found in the [RFC 7231 docs][1]:
If a DELETE method is successfully applied, the origin server SHOULD
send a 202 (Accepted) status code if the action will likely succeed
but has not yet been enacted, a 204 (No Content) status code if the
action has been enacted and no further information is to be supplied,
or a 200 (OK) status code if the action has been enacted and the
response message includes a representation describing the status.
I'm having a hard time interpreting what the 200 response means - is it only kosher to send a string message (Success!) or an object containing a message attribute ({message: "Success!"})? Or can you do whatever you want there? What's the best practice in Express using Mongoose?
Thanks in advance for the help, and sorry for my noobness with HTTP stuff.
[1]: https://www.rfc-editor.org/rfc/rfc7231#section-4.3.5
You should use findOneAndRemove! Something like:
Todo.findOneAndremove({ id: req.params.todoId }, function( error, doc, result) {
// it will be already removed, but doc is what you need:
if (err) res.status(500).send(err);
res.send(doc.id);
});

Is there any difference between using request.body or request.params in node.js?

I am wondering if there is any preference in using request.body or request.params in node.js when sending data from client to server?
You can fit more (diverse) data in the body than in the url. You can pass any string (special characters) in the body, while encoding them in the url would get you vulnerable to status 414 (Request-URI Too Long). And it's a lot easier to use the body when passing arrays and complex objects :)
I would say that a best practice would be that you should use params when doing a get, but use body for post, put and patch.
a sample get
app.get "/api/items/:id", (req, res) ->
itemController.getItem req.params.id, (item, error) =>
if !error
res.send 'item': item
else
res.send 'error: error
a sample post
app.post "/api/items", (req, res) ->
itemController.saveItem req.body, (item, error) =>
if !error
res.send 'item': item
else
res.send 'error: error
You would add validation on as well, but this has been how I have been writing all of my endpoints.
It's been over 4 years since this question was asked, however, I would still chime in to make sure somebody else stumbling upon this post has more information to understand this concept better.
req.body and req.params server two different purposes.
you would use req.body when you need to send data to a server (to store it or something), like in a "POST" request to a server. For instance, check out the following:
You are doing a "POST" request to mongodb to save a blog post. In this scenario, you would want to get the data coming in the body of the request and send it to the db. Here you would use req.body
app.post("/blog", function(req, res){
var data = req.body.blog;
.... // code to send data to the db
....
});
req.params is used when you want to extract a "param" from the url. let's say you want to extract an "id" which is part of the url. For instance "id" is the number in the following url after questions
stackoverflow.com/questions/18187424/
app.get("/xyz/questions/:id", function(req, res){
var useId = req.params.id;
...
...
});
Hope, it helps.
Thanks,
Kartik

Resources