Home > Back-end >  Mongoose redirect not waiting for findByIDAndDelete
Mongoose redirect not waiting for findByIDAndDelete

Time:11-09

I have created a to-do list app using Node, Express and Mongoose: screenshot of to-do list app

To delete a task, the user hits the cross button on the right hand side. This sends a POST request with the task ID to the /delete_task endpoint. The router for this endpoint is /routes/delete_task.js:

var express = require('express');
const Task = require('../models/task');
var router = express.Router();

express.json();

router.post('/', async (req, res, next) => {
    const deleted_task = await Task.findByIdAndDelete(req.body.taskID);
    console.log('Deleted task: \n', deleted_task);
    res.redirect('..');
}
);

module.exports = router;

The router performs a findByIdAndDelete, and then redirects to the home directory. The router for the home directory renders a view of all the existing tasks in the collection, and looks like:

var express = require('express');
const Task = require('../models/task');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  Task.find({}, function (err, result) {
    if (err) {console.error(err)};
    if (result) {
      return res.render('index', {title: 'To-do list', tasks: result})
    };
  });
});

module.exports = router;

My problem is that when deleting a task, the findByIdAndDelete successfully deletes the task, but this is not reflected in the redirected home page. The deleted task only disappears once I refresh the page. This suggests that it's some kind of async issue, and that the redirect is happening before the findByIdAndDelete query has finished executing.

To address this, I have made the router.post() callback an async function and am using await on the findByIdAndDelete, and I have also tried placing the res.redirect('..') in a callback function of the findByIdAndDelete, which also does not fix the problem:

router.post('/', (req, res, next) => {
    Task.findByIdAndDelete(req.body.taskID, (err, result) => {
        if (err) {
            console.error(err)
        };
        if (result) {
            console.log(result)
        };
        res.redirect('..');
    });
});

I have looked for other questions on stackoverflow, all of which seem to suggest that this is an async issue caused by the redirect happening before the query has finished executing. The suggested solutions I have found were to make the router.post(...) callback an async function and await the result of the Mongoose query, or to place the res.redirect('..') in the callback of the findByIdAndDelete so that the redirect happens after the query has finished executing. I have tried both of these but the problem remained.

The only other thing I can think of is that I am trying to redirect from within a POST request, and I don't know if this is legit. It seems to work fine looking at the log (see last 2 lines where the GET request to / follows the POST request to /delete_task):

New task submitted:  cake
New task created successfully:  cake
POST /new_task 302 29.747 ms - 46
GET / 200 4.641 ms - 1701
GET /stylesheets/style.css 304 0.849 ms - -
GET /javascripts/delete_task.js 304 0.479 ms - -
Deleted task: 
 {
  _id: new ObjectId("636a993ca0b8e1f2cc79232a"),
  content: 'cake',
  completed: false,
  __v: 0
}
POST /delete_task 302 10.358 ms - 24
GET / 200 3.867 ms - 1348

This is where I've hit a brick wall and I can't see what might be causing the issue. Really appreciate any help or suggestions anyone might have - cheers.

CodePudding user response:

I don't think this is a problem with asynchronousness, because you wait properly before responding to the POST request.

But the res.redirect makes sense only if hitting the cross button navigates from the To-do list page to the /delete_task page and from there back, by virtue of the redirection. This would be possible only with an HTML <form> element that is submitted upon hitting the button.

Is that how you have implemented it? You say that you "send a POST request", but is this through a <form>, or rather through an axios.post or a similar Javascript method? In the latter case, the following would happen:

  • The Javascript client sends the POST request and the deletion is carried out on the database.
  • The Javascript client receives a redirection response and sends the GET request.
  • The Javascript client receives the HTML page for the to-do list as response, but does nothing with it.

In other words: the To-do list page would not be reloaded by this axios.post request. If you want this to happen, don't respond to the POST request with a redirection, but simply with 200 OK, and have the Javascript client execute location.reload() when it receives this response.

  • Related