In this article you will learn how to use Nodemailer to send your AWS SES emails.
I recommend this approach because the Nodemailer API
- gives you a platform-agnostic interface to build on top of, reducing vendor lock-in
 - is less verbose than the AWS SDK API
 - supports more advanced features such as attachments, out of the box
 
Compare this AWS SDK snippet:
import AWS from "aws-sdk";
async function sendSignInLink({
  to,
  signInLink,
  expiresInDays,
}: {
  to: string;
  signInLink: string;
  expiresInDays: number;
}) {
  const params = {
    Source: `Authentication <noreply@example.com>`,
    Destination: {
      ToAddresses: [to],
    },
    Message: {
      Subject: {
        Charset: "UTF-8",
        Data: "Your Sign-In Link",
      },
      Body: {
        Text: {
          Charset: "UTF-8",
          Data: `Hi there,
This is your sign-in link: ${signInLink}
This link will expire in ${expiresInDays} days.
For security reasons, you shouldn't reply to this email.`,
        },
      },
    },
  };
  await new AWS.SES().sendEmail(params).promise();
}
With its Nodemailer equivalent:
import AWS from "aws-sdk";
import nodemailer from "nodemailer";
async function sendSignInLink({
  to,
  signInLink,
  expiresInDays,
}: {
  to: string;
  signInLink: string;
  expiresInDays: number;
}) {
  const mailer = nodemailer.createTransport({
    SES: new AWS.SES(),
  });
  await mailer.sendMail({
    from: `Authentication <noreply@example.com>`,
    to: to,
    subject: "Your Sign-In Link",
    text: `Hi there,
This is your sign-in link: ${signInLink}
This link will expire in ${expiresInDays} days.
For security reasons, you shouldn't reply to this email.`,
  });
}
Installation
npm install aws-sdk nodemailer
If you're using TypeScript:
npm install --save-dev @types/nodemailer
At some point you may need to specify your AWS Region:
AWS.config.update({ region: process.env.AWS_REGION_ID });
Creating the Nodemailer instance
const mailer = nodemailer.createTransport({
  SES: new AWS.SES(),
});
Attachments example
This is one of the main reasons I like using Nodemailer, it provides a clean facade for trickier use cases such as attachments.
const args = {
  // ...
};
const mailer = nodemailer.createTransport({
  SES: new AWS.SES(),
});
await mailer.sendMail({
  from: `${args.fromName} <${args.fromEmail}>`,
  to: args.toEmail,
  cc: args.ccEmails,
  subject: args.subject,
  text: args.text,
  html: `<div>${args.text}</div>`,
  attachments: [
    {
      filename: args.fileName,
      content: fs.createReadStream(args.filePath),
    },
  ],
});
Switching transport based on environment
let mailer;
if (process.env.NODE_ENV === "production") {
  mailer = nodemailer.createTransport({
    SES: new AWS.SES(),
  });
} else {
  mailer = nodemailer.createTransport({
    port: 1025,
  });
}
