Send AWS SES Emails with NodeJS & TypeScript

main

Here we send a signup confirmation email to a user.

import { getHTMLTemplate, fillHtmlTemplate, sendEmail } from "../email"

export const sendEmailConfirmationLink = async (
  email: string,
  authLink: string
) => {
  const html = await getHTMLTemplate("auth")

  await sendEmail({
    email,
    body: fillHtmlTemplate(html, {
      "auth-link": authLink,
      email,
    }),
    subject: "Log in to Increaser",
    source: `Login <noreply@increaser.org>`,
  })
}

First, we want to take an HTML template. The function takes the template name and reads it from the file. I make template names the same as HtmlTemplate type.

export const getHTMLTemplate = memoize((template: HtmlTemplate) => {
  return readFile(
    path.resolve(__dirname, `../../templates/${template}.html`),
    "utf8"
  )
})

I don't usually send emails with fancy styling to not appear in recipient spam or promotion tabs.

In the auth-email template, we expect two variables - auth-link and email.

<p>Click the link below to sign in to <b>Increaser</b>.</p>

<p>This link will expire in 20 minutes.</p>

<a href={{auth-link}}>Sign in to Increaser</a>

<p>Confirming this request will securely sign you in using {{email}}.</p>

- Increaser Team

Once we've read the file into a string we'll use the fillHtmlTemplate function to insert variables into an email html.

export const fillHtmlTemplate = (
  html: string,
  variables: Record<string, string>
) => {
  let result = html
  Object.entries(variables).forEach(([key, value]) => {
    result = result.split(`{{${key}}}`).join(value)
  })

  return result
}

We pass the recipient email, HTML body, subject, and source to the sendEmail function.

I ran my Lambda in the European region, but AWS SES doesn't work there. So I explicitly pass the Virginia region to the SES constructor.

interface SendEmailParameters {
  email: string
  body: string
  subject: string
  source: string
}

const ses = new aws.SES({ region: "us-east-1" })

export const sendEmail = ({
  email,
  body,
  subject,
  source,
}: SendEmailParameters) =>
  ses
    .sendEmail({
      Destination: {
        ToAddresses: [email],
      },
      Message: {
        Body: {
          Html: {
            Data: body,
          },
        },
        Subject: {
          Data: subject,
        },
      },
      Source: source,
    })
    .promise()

We pass Destination, Message, and Source to the SES method and make it into a promise.