skip to Main Content

Has anybody combined their edit and create forms in a MEVN stack? For example Moongoose’s save method can create or update but I’m not sure how you handle passing in the ID if it does exist vs does not exist. Here is what I currently have for my create process & update process. It would be nice to combine two different componenets into one that did both jobs.

CreateStudent.vue

handleSubmitForm() {
  console.log("handleSubmitForm")
  this.isSubmitted = true;
  let apiURL = "http://localhost:4000/api/create-student";

  axios
    .post(apiURL, this.student)
    .then(() => {
      this.$router.push("/list");
      this.student = {
        firstName: "",
        lastName: "",
        email: "",
        phone: "",
        password: "",
        confirmPassword: "",
      };
      //alert("SUCCESS! " + JSON.stringify(this.userForm));
    })
    .catch((error) => {
      console.log(error);
    })
    .finally((next) => {
       // console.log('is Submiited);
    })
},

studentRoute.route("/create-student").post((req, res, next) => {
    StudentModel.create(req.body, (error, data) => {
        if (error) {
            return next(error);
        } else {
            res.json(data);
        }
    })
})

EditStudent.vue

handleUpdateForm() {
  let apiURL = `http://localhost:4000/api/update-student/${this.$route.params.id}`;

  axios.post(apiURL, this.student).then((res) => {
      console.log(res)
      this.$router.push('/list')
  }).catch(error => {
      console.log(error)
  });
}

studentRoute.route('/update-student/:id').post((req, res, next) => {
    StudentModel.findByIdAndUpdate(req.params.id, {
      $set: req.body
    }, (error, data) => {
      if (error) {
        return next(error);
      } else {
        res.json(data)
        console.log('Student successfully updated!')
      }
    })
})

2

Answers


  1. According to the docs you can simple send in an options to findByIdAndUpdate, one of them is upsert

    [options.upsert=false] if true, and no documents found, insert a new
    document

    https://mongoosejs.com/docs/api/model.html#Model.findByIdAndUpdate()

    So something like

    StudentModel.findOneAndUpdate(
      { _id: req.params.id },
      { $set: req.body },
      { upsert: true } // Make this update into an upsert
    );
    

    should work both as update or insert

    Login or Signup to reply.
  2. It is better to handle updates and creates separately. @Bergur is correct in that findOneAndUpdate and findByIdAndUpdate have an upsert option that will allow you to update or insert if no document matches. However, in my experience this typically works initially and then the problems start.

    The key sticking point is validation. With findOneAndUpdate and findByIdAndUpdate you can also pass the runValidators: true option to tell mongoose to run the validators but this only works on fields included in the update document and only for some operations. So if you have a constraint on a field and you don’t pass that field in the update document then mongoose won’t do any validation. That can have some negative consequences.

    To illustrate a simple schema would look like:

    const studentSchema = new mongoose.Schema({
       firstName: {
          type: String,
          required: true
       },
       lastName: {
          type: String,
          required: true
       },
       veryImportantProperty: {
          type: String,
          required: true
       },
    });
    

    Now if you do a findOneAndUpdate or findByIdAndUpdate like so:

    req.body = {
       firstName: 'Bruce'
       lastName: 'Wayne'
       //< Whoops, veryImportantProperty wasn't included in the post request
    }
    const student = await Student.findByIdAndUpdate(
       req.params.id,
       req.body,
       {
          upsert: true, 
          runValidators: true, 
          new: true
       }
    );
    

    Then no validation is done on veryImportantProperty even with the runValidators option because it wasn’t included in req.body so mongoose will create the document without it.

    Even if you use the $set operator like so:

    const student = await Student.findByIdAndUpdate(
       req.params.id,
       { $set: req.body },
       {
          upsert: true, 
          runValidators: true, 
          new: true
       }
    );
    

    It will still save the new document without veryImportantProperty. With Model.create() you give yourself better data integrity, it’s easier to debug and you can send better error responses back to the user if a create operation fails.

    This is a simple example but with more complex validations things can get messy very quickly and it will be difficult to debug, especially if you revisit your codebase after some time away.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search