Search code examples
javascriptnode.jsapostrophe-cms

Apostrophe CMS - afterInsert is called multiple times


In Apostrophe CMS I have a piece that I wish to be submitable using apostrophe-submit-widgets - and everything seems to be working, except that my afterInsert method is called twice and thus sends twice the amount of emails.

My configuration looks like this:

const nodemailer = require('nodemailer')
const colors = require('colors')

module.exports = {
  extend: 'apostrophe-pieces',
  name: 'request-form',
  label: 'Request Form',
  alias: 'requestForm',
  //...
  ],
  construct: function (self, options) {
    self.beforeSave = function (req, piece, options, callback) {
      piece.title = piece.name + ' ' + piece.email
      piece.published = true
      return callback()
    }
    self.afterInsert = async function (req, piece, options, callback) {
      const transporter = nodemailer.createTransport({
        //transport config
      });

      function handleError(err) {
        console.error(err.cyan)
        return callback(err)
      }

      const messageToAdmin = {
        //nodemailer message config
      }


      const messageToUser = {
        //nodemailer message config
      }

      await transporter.sendMail(messageToAdmin)
        .then(transporter.sendMail(messageToUser))
        .catch(handleError)

      return callback()
    }
  }
}

Do you have any idea why it might be working like that? The only thing that I was able to find out with debug tools is that the afterInsert is indeed called twice, it's not something inside it that's looped. It happens both when I use request-forms-submit-widgets and when I add it from the admin bar.

Edit: As per suggestion I removed then chaining and used instead this:

try {
  await transporter.sendMail(messageToAdmin)
  await transporter.sendMail(messageToUser)
  callback()
} catch (err) {
  console.error(err)
  callback(err)
}

Unfortunately, it did not help.


Solution

  • If you are using apostrophe-workflow, you can expect more than one call to afterInsert because the document must be inserted separately into each locale.

    If you are not using localization, you might wonder why this is necessary. The answer is that ordinary edit/submit/commit workflow still uses two locales, called default and default-draft.

    If workflow is appropriate for the content that is being submitted, for instance it will be further edited as a draft before it is approved and committed, then you should account for this in your afterInsert handler. You can look at piece.workflowLocale to figure out which locale you're dealing with.

    If workflow is not necessary for this content type, just add it to the excludeTypes array option in your configuration of the apostrophe-workflow module. Just one thing to keep in mind there: types excluded from workflow cannot join with types that do have workflow. However you can do the reverse (types with workflow can join with types that are excluded). This restriction will likely be lifted in 3.x.