Does nodejs supports updating many collection in one router? - node.js

I tried updating my two collections in one router but it keeps on giving me error:
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:494:11)
at ServerResponse.setHeader (_http_outgoing.js:501:3)
Here is what I did:
api.js
router.put('/editbloodrequest', function(req, res) {
//this works
Bloodrequest.findOne({ _id:"5c1d2c8c68503b0adceffa92"}, function(err, bloodrequest) {
if (err) throw err;
bloodrequest.request_status = "claimed";
});
//but after inserting this, it gives me error
Blooddonation.findOne({ _id:"5c00fa03dadb0c3b00739dd9"}, function(err, blooddonation) {
if (err) throw err;
blooddonation.blood_group = "test";
});
});

First, I strongly suggest that you abandon callbacks and use Mongo's Promise behavior instead. Unless you intended to by executing both requests concurrently, this would be the way you would express this using promises:
Bloodrequest.findOne({ _id:"..." }).then(bloodrequest => {
bloodrequest.request_status = "claimed";
})
.then(() => Blooddonation.findOne({ _id:"..." }).then(blooddonation => {
blooddonation.blood_group = "test";
});
Second, I'm guessing you cut some code from your example; nowhere in there do you actually respond (using res). Based on the error, I'm guessing you attempted to set res in both of your callbacks. You can only respond once, that error typically means you already redirected / responded / etc. and now are attempting to do it again.
Usually you'll do this:
Bloodrequest.findOne({ _id:"..." }).then(bloodrequest => {
bloodrequest.request_status = "claimed";
})
.then(() => Blooddonation.findOne({ _id:"..." }).then(blooddonation => {
blooddonation.blood_group = "test";
}).then(() => {
// set your positive res response
}).catch(error => {
// set your error res response
// since you're using promises, if any of the above steps fail, you'll
// end up here.
});

You need require model of collections which you want to update in api.js
In route handler function, you need a transaction among related collection. You can you fawn npm to make this easier
https://www.npmjs.com/package/fawn

Related

Mongoose document.save() method is blocking websocket execution

I have a NodeJS app running a websocket server using the ws npm package. In a callback from the message event ws.on("message", async (rawData, isBinary) => {}), I'm trying to update a document and then save it. The .find() method works fine, but the .save() method blocks execution completely.
const users = await Promise.all(
game.players.map((p) => User.findById(p.id)) // This works fine
);
// mess with users here
await Promise.all(users.map(u => u.save())) // <-- This doesn't work
await users[0].save() // <--- This doesn't work either
users[0].save((err, doc) => {
// This doesn't work either
})
users[0].save().then(doc => {
// console.log(doc) // <-- This doesn't work either
})
For starters, is this even possible ? Or am I thinking about it the wrong way and should I trigger some kind of POST request from the client to hit my http server instead to do the mongodb operations ? In any case, I'm struggling to understand why this is not possible.
Also something interesting is when I tried logging everything everywhere I tried to do the following
console.log(users[0].save())
users[0].save((err, doc) => {
if (err) console.log(err) // <-- no log here
console.log(doc) // <-- no log here
})
which threw mongoose's ParallelSaveError: Can't save() the same doc multiple times in parallel. So I'm guessing the action does take place but not entirely ?
Any insight on this is welcome.

JSON array from Express route is undefined in React console

I am currently working on a web app to manage an external database. I am not very familiar with express or NodeJS at this point so I wanted to ask how to send a JSON object to the client sides console without getting undefined?
I have this function to connect then select the what I need and afterwards I converted my JSON object to an array of JSON objects. It displays the data fine in the console as well.
async function connect() {
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (err) {
console.error('Unable to connect to the database:', error);
}
info = await sequelize.query('select * from LeadsInformation', { type: QueryTypes.SELECT });
const details = JSON.stringify(info);
console.log(details);
detailsArray = JSON.parse(details);
console.log(detailsArray);
}
Everything works fine in here, I can get the data and display it in the terminal.
This is my GET route:
app.get("/list", (req, res) => {
connect();
res.json(detailsArray)
});
I have tried a couple of suggested ways based on other explanations and code snippets but none of them has worked so far so I left it like that. I thought foreaching through the data itself in the request would be a solution but it did not work. I also tried using the JSON itself and trying to display it and also tried using the body parser library. Though the library has not been updated for two years. Also I am using axios to fetch the data. It works fine when I try sending a simple string like "hello world" for example.
Is there anything that I'm missing or do you have any other solutions? I would also appreciate an explanation as well if possible.
Edit: It might also have to do something with how I am getting the response in the frontend. I'll look into that as well and will update this thread if I sort it out!
This is the way I get the response. I am currently trying to show in the console. I am using axios API.
Axios({
method: "GET",
url: "http://localhost:5000/list",
headers: {
"Content-Type": "application/json"
}
}).then(res => {
console.log(res.data.json);
});
Probably you have undefined in route because connect function doesn't return anything.
Also connect is an async function it means that it returns Promise and you have to call .then method or use await to get value from it.
Here is the code snippet with fixes that I described above.
async function connect() {
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (err) {
console.error('Unable to connect to the database:', error);
}
info = await sequelize.query('select * from LeadsInformation', { type: QueryTypes.SELECT });
const details = JSON.stringify(info);
detailsArray = JSON.parse(details);
return detailsArray;
}
app.get("/list", async (req, res) => {
const result = await connect();
res.json(result)
});
Notice that in the router handler function I also use async and await because I call connect which is an asynchronous function.
The solution above did work and also another problem I had was that I wasn't getting the response correctly.
I ended up getting the response to the frontend after changing my code to the following from:
console.log(res.data.json);
To:
console.log(res.data[1]);

Sign up and log in, Express, React, Node, Psql

I am trying to create signup and Login for the first time with express and react using PostgreSQL. My post works just fine. A user can be added to the database so I jumped into handling duplicates.
I am using the findUserByEmail function to find my email and then, in my routes, create the user if it does not exist.
I tried everything and still is giving me problems. I manage to get it working by just returning the query, without a response, which I don't think is right:
const findUserByEmail = (req, response) => {
return pool.query("SELECT * FROM users WHERE email = $1", [req.body.email])
};
Although, I need the response to handle the errors.
The way that I found more common and is how I am trying is:
const findUserByEmail = (req, response) => {
pool.query("SELECT * FROM users WHERE email = $1", [req.body.email]),
(error, results) => {
if (error) {
throw error;
}
response.json(results.rows);
};
};
And when I call it here:
app.post("/signup/user", (req, res, next) => {
queries
.findUserByEmail(req, res)
.then(user => {
if (user.rows.length > 0) {
res.status(400).send("this email is already in use");
} else {
queries.createUser(req.body, res);
}
})
.catch(err => {
console.log(err);
res.status(500).send("Something went wrong");
});
});
But the error is:
Cannot read property 'then' of undefined
If anybody can give me a hand cause I've been 2/3 weeks just for the authentication.
I'll leave my repo if anybody wants to have a look, is a bit messy though.
https://github.com/jaitone/CRUD-in-JS
Thank you!
if you are using pg as part of your project. then:
const findUserByEmail = (req, response) => { // send just email instead
return pool.query("SELECT * FROM users WHERE email = $1", [req.body.email])
};
Is completely legal and beautiful. The library creates a promise and returns it.
I manage to get it working by just returning the query
It is not returning the query, it is returning the mechanism to run the query in a promise wrapper(to be run in the future). So when you do .then it will actually execute and return the result. BUT
If you want to do it manually:
In the findUserByEmail you are not returning a Promise, instead you are just ending the request chain by saying res.json(which in turn means you are returning undefined).
You can create a Promise wrapper or use util.promisfy to make the pool.query a promise.
const findUserByEmail = (req, response) => { // send just email instead
return new Promise((resolve, reject)=>{
pool.query("SELECT * FROM users WHERE email = $1", [req.body.email]),
(error, results) => {
if (error) {
reject(error);
}
resolve(results.rows);
};
});
};
Note, sending the email instead of whole req and res objects is a good idea.

Respond to Koa request immediately, continue middleware chain

I am writing the middleware for API endpoints in my app that respond to webhooks from other applications, and am relatively new to Koa, so am not completely familiar with its patterns.
I would like to structure my middleware as follows:
exports.updateReceived = async (ctx, next) => {
// Respond to server issuing the webhook
ctx.res.body = "ok";
ctx.res.statusCode = 200;
// Grab what we need from the request
const { headers, state, request } = ctx;
const { body } = request;
// Do some async work
const { example } = await doSomeAsyncWork(ctx);
// Prepare a database query
const query = { aValue: anId };
// Run the DB query
const result = await Thing.findOne(query);
// Add data to the request
state.thing = result;
// Move on...
return next();
};
However, this does not appear to be working, as an error in any of my async methods can cause the route to error out.
My goal is for this endpoint to always respond "yep, ok" (immediately), meaning it is simply up to the application to handle any error states.
I have researched this fairly well, and have come across this pattern:
app.use(async ctx => {
db.fetch() // Assuming a Promise is returned
.then(() => { ... })
.catch(err => {
log(err)
})
// status 200 will be returned regardless of if db.fetch() resolves or rejects.
ctx.status = 200
})
However, this does not meet my needs as the middleware makes no use of next, so it is not really a useful pattern, so far as I can tell.
Could someone tell me what I am overlooking?
next() invokes the downstream middleware chain and returns a promise that resolves after all downstream middleware/handlers have finished.
That means you can simply implement your own upstream error handler that catches any errors and always ensures a 200 OK response.
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx, next) => {
next().catch((err) => {
// Print error for our own records / debugging
console.error(err)
// But ensure that outgoing response is always a smile
ctx.status = 200
ctx.body = ':)'
})
})
app.use(async (ctx) => {
// Do your webhook / biz logic here, but for demonstration
// let's make it always throw an error. Thus upstream next()
// will be a rejected promise.
throw new Error('this middleware will always bubble up a rejected promise')
})
app.listen(3000, () => {
console.log('listening on 3000')
})
Note: We are not awaiting next(), so we can end the request immediately. However the next handler in the chain will still have the opportunity to process the logic
app.use((ctx, next) => {
next()
ctx.status = 200
})
app.use( async ctx =>{
db.fetch()
.then(() => { ... })
.catch(err => log(err))
}
}
Just to divert the solution in a different side, You could consider adding your work to some kind of MessageQueue and then let another process do that task for you. Basically asynchrously but you will still be important. This kind of pattern suits for your requirement.
There are many messaging system availble like AWS SQS which you could consider. This way your api will be very light weight and it will do thing which it needs to and send a command to your messaging system to do extra stuff. You are basically separting your core logic and the doing things in background which scales very nicely as well.

ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client at ServerResponse

I am trying to create a simple REST API with NodeJS and Express without any database. I have stored all of my data in JSON files.
The data is in the form of an array of objects.
I have paths like fund-name/:portId
so I am doing this:
const fundName = require('./json/fund-name.json');
app.get('/fund-details:portId', (req, res) => {
const portId = req.params.portId;
fundDetails.forEach(fund => {
if (fund.portId === portId) {
return res.json(fund);
}
return res.json([]);
});
});
when I hit the url http:localhost:3000/fund-details/1234, I get the following error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at ServerResponse.header (/home/username/Desktop/data-server/node_modules/express/l
ib/response.js:767:10)
It works fine when I don't pass any path param to get all the funds.
Where am I going wrong??
This error is because you are using res.send() multiple time in single api call.
Correct way
if(a){
res.send()
}else{
res.send()
}
Wrong way
if(a){
res.send()
res.send()
}else{
res.send()
}
In your code.
app.get('/fund-details:portId', (req, res) => {
const portId = req.params.portId;
fundDetails.forEach(fund => {
if (fund.portId === portId) {
return res.json(fund); // many or single times here
}
return res.json([]); // and here when fund !==portId here
});
});
You can try
app.get('/fund-details:portId', (req, res) => {
const portId = req.params.portId;
var flag
var data = []
fundDetails.forEach(fund => {
if (fund.portId === portId) {
flag=true
data.push(fund)
}
});
if(flag){
res.send(data);
}else{
res.send()
}
});
The method res.json(fund) is called per each item in fundDetails and then a further res.json([]) method is called. This leads to your response being send back multiple times (which it shouldn't happen, only 1 response per a single api call should be used).
I suggest that you use an array and push back objects with the matching port id and then send the array back to the user when the operation is completed. To be honest, you don't even need the flag variable to check if funds exists or not since if they don't, you empty data array is sent back.
var data = [];
fundDetails.forEach(fund => {
if (fund.portId === portId)
data.push(fund);
});
res.json(data);

Resources