Search code examples
expressmongoosecallbackresponsemongoose-schema

Express + Mongoose: Cannot send response in callback


I've been frustrated with this issue for more than 24 hours and I've narrowed down the issue to this:

In the following code, I'm updating a MongoDB document using mongoose and upon a successful update, I return a json object via an express response. I get the following error when I try to call a response in the in the callback from the mongoose.

The update is successful in the database, I get the data back from the callback, and everything works as it should, except for sending the response saying everything is great.

I do not send a responses before this nor do I touch the res object in any way prior to sending. I am seeking help on how to send a response upon successful callback.

Error:

Updating user draftBooks
/home/samuel/Documents/Github/Moirai/node_modules/mongoose/lib/utils.js:419
        throw err;
        ^

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:356:11)
    at ServerResponse.header (/home/samuel/Documents/Github/Moirai/node_modules/express/lib/response.js:719:10)
    at ServerResponse.send (/home/samuel/Documents/Github/Moirai/node_modules/express/lib/response.js:164:12)
    at ServerResponse.json (/home/samuel/Documents/Github/Moirai/node_modules/express/lib/response.js:250:15)
    at Promise.<anonymous> (/home/samuel/Documents/Github/Moirai/server/lib/stories.js:105:13)
    at Promise.<anonymous> (/home/samuel/Documents/Github/Moirai/node_modules/mongoose/node_modules/mpromise/lib/promise.js:162:8)
    at emitOne (events.js:77:13)
    at Promise.emit (events.js:169:7)
    at Promise.emit (/home/samuel/Documents/Github/Moirai/node_modules/mongoose/node_modules/mpromise/lib/promise.js:79:38)
    at Promise.fulfill (/home/samuel/Documents/Github/Moirai/node_modules/mongoose/node_modules/mpromise/lib/promise.js:92:20)

Mongoose Schema:

var Schema = mongoose.Schema;
// create user schema
var userSchema   = new Schema({
    user_id: String,
    username: String,
    password: String,
    draftBooks: [{book_id: String}]
});

// add draftBook
userSchema.methods.addDraftBook = function (book_id, callback) {
  return this.model('User').findOneAndUpdate(
    { _id: this.user_id },  // search query
    { $push: { 'draftBooks': {'_id': book_id.toString()} } }, // update action
    { upsert: true },
    callback
  );
}

Express Route:

 router.route('/')
  .post((req, res, next) => {
   // other stuff that works great
   // req.storyData defined here
  })
  .post((req, res) => {
      // create update corresponding user
      console.log("Updating user draftBooks");
      // res.json({}) works outside of callback
      var userData = new user({ user_id: req.storyData.creator });
      userData.addDraftBook(req.storyData._id, (err, data) => {
        if (err){
           res.json({message: err});
        } else {
           res.json({message: "Should work"}); //gives error
        }
      });
  });

This has left me quite frustrated, I don't see anything syntactically wrong, what am I missing?


Solution

  • your problem is that this line :

    res.json({message: "returns this response"})
    

    runs before

    res.json({message: "Gives error"}).
    

    so you already set the json inside res, that why you get the error. you cannot set the header twice. it doesn't make much sense to give value to res.json() twice (1 inside the callback and 1 outside)

    why not do this :

         userData.addDraftBook(req.storyData._id, (err, data) => {
        if (!err)
            res.json({message: "successful data"})
        else
           res.json({message: "returns this response"})
      });