My front-end app makes requests to my Express.js app. The Express app uses the official MongoDB Node.js driver to make requests to my (Atlas) MongoDB. When the front-end app cancels a request (e.g. AbortController, CancelToken), how do I make the Express endpoint kill the corresponding MongoDB request?
Relevant endpoint code:
export default async function search(req, res) {
try {
const payload = await collection.aggregate(pipeline).toArray()
return res.status(200).send(payload)
} catch ({ message, stack }) {
return res.status(400).send({ message, stack })
}
}
CodePudding user response:
There are two ingredients to this:
First, you need an API method that allows you to abort the database request. The MongoDB Node.js driver contains a method cursor.close
that sounds to me as if it did what you need. But you should check the documentation whether there are better methods.
Second, your middleware must detect when the request or response stream is closed by the front-end app. This must be distinguished from the close
event after everything has been sent successfully (otherwise the "close cursor" operation would be attempted after every database request that has been completed). The following code achieves this:
app.use(async function(req, res) {
let destroyed;
const cursor = await collection.aggregate(pipeline)
req.on("close", function() {
if (!this.complete) {
destroyed = true;
cursor.close();
}
});
res.on("close", function() {
if (!this.writableEnded) {
destroyed = true;
cursor.close();
}
});
const payload = await cursor.toArray();
if (!destroyed)
return res.status(200).send(payload);
});
You can probably omit the req.on(...)
, because the request body is so small that it is unlikely that the request is canceled while it is still being sent. But the res.on(...)
will be called when the request is canceled while waiting for the response to finish.
The destroyed
variable is meant to ensure that no attempt is made to send a response if the request has been canceled anyway.