Expressjs router with Firestore resolves before data is retrieved - javascript

EDIT: I found out that "async" could be added to the function signature, and while the output does go in the proper order, I still get an error that I can't set headers after they are sent, even though I am not setting them anywhere else. I've modified the code to reflect this.
I'm having an issue with my Google Firestore api using ExpressJS. It seems that the result is sent before the Firestore query completes and I'm not sure why, as I'm not doing anything with res otherwise. It seems that Firestore queries are async but I don't know how to have my endpoint wait for the Firestore data before sending results. Here is my router code:
router.post('/some_endpoint/get_something', async function (req, res) {
console.log("Getting firestore data...")
let db_data = null;
let some_val = req.body.some_val;
let colRef = db.collection("some_collection");
await colRef.where("some_field", "==", some_val)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log("Still processing...")
db_data = doc.data()
})
res.json({ <---- This is where it breaks
status: 200,
data: db_data
})
})
.catch(function(error) {
console.log("Error getting doc: ", error);
})
console.log("We're done!")
});
This is the output order (EDIT with new output order):
Getting firestore data...
Still processing...
Error getting doc: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client...
We're done!

The error message is telling you that you're trying to send multiple responses, which is not valid. Here, your code is calling res.json() many times, once for each document in the query results:
querySnapshot.forEach(function(doc) {
console.log("Still processing...")
res.json({
status: 200,
data: doc.data()
})
})
You should only call res.json() once with the final result to send to the client, after you're done iterating the results.

Related

How to get a variable from front to a service worker?

Some context
I've created a service worker to send notifications to registered users.
It works well until I tried to implement a sort of id to each people who register to a service worker (to send notification).
I do that because I have to delete old registration from my database, so I took the choice to let each users three registration (one for mobile device and two others for different navigator on computer) and if there is more, I want to remove from the database the older.
Tools
I'm using nodejs, express and mySql for the database.
The issue
When I launch a subscription I got this error:
SyntaxError: Unexpected token o in JSON at position 1
at JSON.parse (<anonymous>)
I saw in an other post that it's because they try to JSON.parse what's already an object.
But in my case, I can't find where I parse, see the part which are concerned:
// service.js (service worker file)
// saveSubscription saves the subscription to the backend
const saveSubscription = async (subscription, usrCode) => {
const SERVER_URL = 'https://mywebsite:4000/save-subscription'
subscription = JSON.stringify(subscription);
console.log(subscription); // I got here what I expect
console.log(usrCode); // <-------------------------------- HERE I GOT UNDEFIND
const response = await fetch(SERVER_URL, {
method: 'post',
headers: {
'Content-Type' : 'application/json',
},
body : {
subscription: subscription,
usrCode: usrCode
}
})
return response
}
But when I console.log(usrCode) in my inspector, I got the good value.
So how should I do to get the value in service.js
Maybe the problem is from:
const bodyParser = require('body-parser')
app.use(bodyParser.json())
At the beginning I thought that the issue is from the back (because I'm not really good with async function).
And here is the back, If maybe I got something wrong.
// index.js (backend)
// Insert into database
const saveToDatabase = async (subscription, usrCode) => {
// make to connection to the database.
pool.getConnection(function (err, connection) {
if (err) throw err; // not connected!
console.log(usrCode);
console.log(subscription);
connection.query(`INSERT INTO webpushsub (webpushsub_info, webpushsub_code) VALUES ('${subscription}', '${usrCode}')`, function (err, result, fields) {
// if any error while executing above query, throw error
if (err) throw err;
// if there is no error, you have the result
console.log(result);
connection.release();
});
});
}
// The new /save-subscription endpoint
app.post('/save-subscription', async (req, res) => {
const usrCode = req.body.usrCode; // <------------------ I'm not sure about this part
const subscription = req.body.subscription
await saveToDatabase(JSON.stringify(subscription, usrCode)) //Method to save the subscription to Database
res.json({ message: 'success' })
})
By searching on google, I've found this tutorial. So the reason why usrCode is undefined is because the service worker doesn't have access to a data stored in front.
First you have to pass it in the URL as following:
// swinstaller.js (front)
// SERVICE WORKER INITIALIZATION
const registerServiceWorker = async (usrCode) => {
const swRegistration = await navigator.serviceWorker.register('service.js?config=' + usrCode); //notice the file name
return swRegistration;
}
And then get it in the service worker:
// service.js (service worker file)
// get the usrCode
const usrCode = new URL(location).searchParams.get('config');

Error while trying to get realtime data from firestore database code: 'ERR_HTTP_HEADERS_SENT'

i am trying to build an app and i am very new to all this. So I've built a very simple function to get data from firestore and it works fine from a moment. This is the code:
async getData(req, res) {
const dataRef = db.collection(`${req.body.banco}`)
let result = []
dataRef.onSnapshot(docSnapshot => {
docSnapshot.forEach(doc => {
const data = doc.data()
result.push(data)
})
console.log(result)
return res.status(200).send(result)
}, (error) => {
console.log(`Erro encontrado: ${error}`)
})
}
My problem is that when I try to update any field from the document. It gets updated but I end up receiving this error:
node:_http_outgoing:576
throw new ERR_HTTP_HEADERS_SENT('set');
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:372:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:174:12)
at ServerResponse.json (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:278:15)
at ServerResponse.send (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\express\lib\response.js:162:21)
at C:\Users\paulo\Desktop\sirius-back\functions\src\controller\createDocs.js:70:40
at QueryWatch.onNext (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\#google-cloud\firestore\build\src\reference.js:1914:13)
at QueryWatch.pushSnapshot (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\#google-cloud\firestore\build\src\watch.js:469:18)
at QueryWatch.onData (C:\Users\paulo\Desktop\sirius-back\functions\node_modules\#google-cloud\firestore\build\src\watch.js:353:26) {
code: 'ERR_HTTP_HEADERS_SENT'
}
the app crashes and i have to start it over.
What is the reason of this? How can i get realtime updates from firestore databse after I update something from?
In Express you handle each requests by sending a single response to it. Since res a a response object, you can only send a response to the caller once. But since you're using onSnapshot, your code gets called for every change to the data too.
So initially, you load the data and send a response to the caller and all is 👍
But then when an update is made to the database, your code executes again and tries to send another response to the caller - and this is when you get an error.
The solution is to read the data only one with something like:
async getData(req, res) {
const dataRef = db.collection(`${req.body.banco}`)
let result = []
dataRef.get().then((docSnapshot) => {
docSnapshot.forEach((doc) => {
const data = doc.data()
result.push(data)
})
console.log(result)
return res.status(200).send(result)
}, (error) => {
console.log(`Erro encontrado: ${error}`)
})
}
Or a bit simplified:
async getData(req, res) {
const dataRef = db.collection(`${req.body.banco}`)
dataRef.get().then((docSnapshot) => {
const result = docSnapshot.docs.map((doc) => doc.data());
return res.status(200).send(result)
}, (error) => {
console.log(`Erro encontrado: ${error}`)
})
}
Once you've called res.send(...) the request is complete and the client stops listening. Sending further updates to a response is not possible with an Express request/response model like you're using.
Also see:
How to send multiple responses while computing in Express.js?
Sending multiple responses(res.json) with the same response object in Express.js
and more from searching for how to send multiple responses for a single request in express
You'll need to choose an infrastructure that allows a client to keep listening. Heads up: building something like that is quite involved, and you're likely better off if you use the client-side Firestore SDK to implement such realtime listeners

mocha/chai testing hangs on await tojson().fromFile, postman passes

So, basically I am developing my first MERN stack project in which I have an api endpoint which resets the collection vehicles. I delete the data from the collection, then I parse the data from a csv and convert them to json and insert the new data. When running my backend code, everything runs smoothly in postman and robo3T. But when I run my testing code, the execution hangs at the instruction:
const vehicles_model = await tojson().fromFile(vehicles_csv);
and consequently the testing fails (res.status 500 is returned). Also, while running the testing code, the endpoint fails to run in postman, as well. What am I doing wrong?
PS: It is my first time writing on stackoverflow, if I haven't made the problem clear, I'll be happy to elaborate :)
this is my endpoint
exports.postResetVehicles = async (req, res) => {
const vehicles_csv = "vehicles.csv";
try {
Vehicles.deleteMany({}).then(function () {
console.log("Data deleted and");
});
const vehicles_model = await tojson().fromFile(vehicles_csv);
Vehicles.insertMany(vehicles_model).then(function () {
console.log("data inserted"); // Success
console.log("=>Data reseted");
}).catch(function (error) {
console.log(error); // Failure
});
return res.status(200).json({ "status": "OK" });
} catch (err) {
return res.status(500).json({ "status": "failed", "error": err });
}
};
and this is my testing code
describe("Testing 'admin/resetvehicles'", () => {
it("should succeed if Vehicles collection is reset", async () => {
const response = await request(app).post(
"/interoperability/api/admin/resetvehicles"
);
expect(response.status).to.equal(200)
});
});

Is it good practice to access req.query in a PUT request?

I'm building a website with an API using NEXTjs. For a single element, I use the dynamic api route provided by NEXTjs and I'm currently using that route both for getting an element and updating element.
In both the GET and PUT request, I use the req.query.fetchId to get or update the element.
However, I see req.query mostly used for GET requests and in POST/PUT request it's usually req.body being used.
It seems to work, but I'm wondering if I should?
This is the URL for the request: api/items/[fetchId]
And this is my code for the PUT request so far:
if (req.method==="PUT") {
try {
const { db } = await connectToDatabase();
const videoGamesCollection = db.collection("videogames");
const result = await videoGamesCollection
.updateOne({ _id: ObjectId(req.query.fetchId) }, {$inc: {}})
res.status(200).json({ message: "success", result: result });
} catch (error) {
res.status(error.code ?? 502).send({
message: error.message ?? "Something went wrong.",
});
}
}

PG-Promise Proc Erroring Out with Unknown Parameter Type

We are attempting to write a PostgreSQL Procedure to insert data into a table. We have created the procedure and ran said procedure with the variables below and it inserts just fine. However, when we try to use pg-promise with our express server, our string parameters are being read as unknown. When we iterate over the post body, we see that each parameter is reading as the type we expect to go in, and PostgreSQL is reading integer correctly, but it isn't reading string correctly. I've attempted to use the as.text function and it sends in the parameter value as "''" but that still reads as unknown. Is there something we are missing to successfully post the data?
let createInspection = async (req, res, next) => {
try {
let params = [];
for (let prop in req.body) {
console.log(typeof req.body[prop]);
params.push(req.body[prop]);
}
console.log(params)
let data = await db.proc('Inspections_Create', params);
res.status(200)
.json({
status: 'success',
data: data,
message: 'Inserted Inspection'
});
}
catch (error) {
return next(error);
}
}

Categories

Resources