How We Send Emails with RedwoodJS + Faktory

We recently started with Redwood for an MVP, and we’re really liking it so far. Thought I would share how we are sending emails with RedwoodJS + Faktory, inspired by ActionMailer

After following these guides, Creating a Background Worker with Exec and Faktory and Sending Emails, I wanted the mailer to be more reusable and remove duplication. To do this, we started with setting up an ApplicationMailer in lib/mailers/applicationMailer.ts.

export default class ApplicationMailer {
  to: string

  constructor(to) {
    this.to = to
  }

  sendEmail = async (template, emailArgs) => {
    const html = await generateHTML(template, emailArgs)
    const text = await generateTextFromHTML(html)

    const emailOptions = {
      to: this.to,
      subject: emailArgs.subject,
      text,
      html,
    }
  }
}

const generateHTML = async (template, emailArgs) => {
  ...
}

const generateTextFromHTML = async (html) => {
  ...
}

Then, we created a UserMailer in lib/mailers/user.ts that extends ApplicationMailer for sending any emails related to users

export default class UserMailer extends ApplicationMailer {
  confirmationEmail = async ({
    firstName,
    confirmationToken,
  }: ConfirmationEmailOptions) => {
    const confirmationURL = `${process.env.MAILER_URL}/confirm/${confirmationToken}`
    const emailArgs = {
      subject: 'Please confirm your email',
      firstName: firstName,
      confirmationURL: confirmationURL,
    }

    this.sendEmail(ConfirmationTemplate, emailArgs)
  }
}

confirmationEmail is just one of the functions we have in UserMailer, we can add as many as we want with minimal effort now. The UserMailer is only responsible for building arguments needed for the email we want to send, and sendEmail takes care of the rest.

Now when we want to send an email from a service or function, we can do something like:

const mailer = new UserMailer(user.email)
const emailArgs = { firstName: 'firstName', confirmationToken: 'confirmationToken' }

await mailer.confirmationEmail({ ...emailArgs })

rather than defining a function to build emails in a service.

Next step was to include Faktory, which after following the guide above required some extra code in ApplicationMailer.sendEmail.

export default class ApplicationMailer {
  ...

 const emailOptions = {
   ...
 }

 const client = await faktory.connect()
 await client.job('sendEmailTask', { ...emailOptions }).push()
 await client.close()
}

And adding the sendEmailTask in lib/tasks/mailer.ts which follows the same method in the Sending Email tutorial above.

Now we have a reusable mailer that is easy to add new emails to, and a bit closer to ActionMailer style which I’m used to.

I hope this helps anyone looking to send emails / organise email structure in Redwood.js, this is my first post in the community so any feedback (good or bad) would be great.

Note: We’ve also been using Maizzle to generate email templates which has been great. We put email templates in lib/mailers/[resource]/templates and then import them in the mailer we need. Here’s an example for reference.

type ConfirmationTemplateOptions = {
  firstName: string
  confirmationURL: string
}

export const ConfirmationTemplate = ({
  firstName,
  confirmationURL,
}: ConfirmationTemplateOptions) => {
  return `
    [template html here]
    ...
   `
}
4 Likes

What a great first post!

How to email is a common question and having the mails sent via a job/q is totally the way to go – to ensure delivery via retries etc.

I hadn’t heard of https://maizzle.com … would you consider a follow up on how to write templates using it … and maybe even adding a How To to the docs as an update/alternative approach to Sending Emails | RedwoodJS Docs?

1 Like

Also, @crabbits have you seen Ryan Chenkie’s post about How to Use Queues with RedwoodJS | Ryan Chenkie

FYI Mora about Maizzle here https://maizzle.com … you can use TailwindCss and even has templates ready for you Netlify Identity mails. Nice.

1 Like

Thanks for the link @dthyresson could only add two for my first post.

Blockquote
would you consider a follow up on how to write templates using it … and maybe even adding a How To to the docs as an update/alternative approach to Sending Emails | RedwoodJS Docs?

Yep definitely, I’ll write a follow up this weekend

Blockquote
Also, @crabbits have you seen Ryan Chenkie’s post about How to Use Queues with RedwoodJS | Ryan Chenkie

@dthyresson I haven’t but I will 100% take a look thanks!

Hey @crabbits this is a great post, thanks! I’m solving a similar problem right now, so it greatly informed my solution.

I have a question though, what’s the benefit of using Faktory for sending emails with nodemailer?

I can understand using it for a long running process, but sending an email should be pretty quick right? I might be missing something.