Skip to content

production ready email system with aws

Updated: at 11:12 AM (5 min read)

Building a Production-Ready Email System with AWS SES and Serverless Stack

Table of contents

Open Table of contents

Introduction

In today’s digital landscape, reliable email communication is a cornerstone for any modern application. Whether it’s for user notifications, password resets, or marketing campaigns, having a robust email system is crucial. Amazon Web Services Simple Email Service (AWS SES) offers a cost-effective, scalable solution for email delivery. Coupled with the power of the Serverless Stack (SST), developers can efficiently manage infrastructure while focusing on application logic.

In this blog post, we’ll explore how to build a production-ready email system using AWS SES and SST, leveraging serverless architecture to reduce operational overhead.

Introduction to AWS SES

AWS SES is a cloud-based email sending service designed to help digital marketers and application developers send marketing, notification, and transactional emails. Key features include:

Detailed Breakdown of the Solution

Stack Setup

To get started, we’ll set up our stack using SST constructs. Below is the initial setup:

import { EventBus, StackContext } from "sst/constructs";

export function Events({ stack }: StackContext) {
  const bus = new EventBus(stack, "event-bus", {
    defaults: {
      retries: 0,
    },
  });

  bus.subscribe("send.email", {
    handler: "packages/functions/src/events/send-email.handler",
    logRetention: "one_day",
    copyFiles: [{ from: "templates", to: "templates" }],
    permissions: ["ses"],
  });

  return { bus };
}

Email handling

The send.email Event The send.email event is triggered whenever an email needs to be sent. It acts as a decoupled mechanism to handle email sending without blocking the main execution flow.

The sendEmail.handler Function

import { Emails } from "@email-service/core/email";
import { EventHandler } from "sst/node/event-bus";
import EmailService from "@email-service/core/emailService";
export const handler = EventHandler(Emails.Events.Send, async evt => {
  console.info("email sending requested", evt);

  const emailService = new EmailService();

  console.log("Sending email", evt.properties);

  await emailService.sendEmail(
    [evt.properties.to],
    evt.properties.from,
    evt.properties.emailType,
    evt.properties.subject,
    evt.properties.data
  );
  console.info("Email sent successfully via EmailService");
});

Email Service Class

The EmailService class encapsulates the logic for sending emails using AWS SES.

import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses";
import * as Handlebars from "handlebars";
import fs from "fs";
import path from "path";

class EmailService {
  private sesClient: SESClient;

  constructor() {
    this.sesClient = new SESClient({});
  }

  public async compileTemplate(
    templateName: string,
    data: any
  ): Promise<string> {
    const templatePath = path.join(
      process.cwd(),
      `templates/${templateName}.html`
    );
    const templateContent = fs.readFileSync(templatePath, "utf8");
    const template = Handlebars.compile(templateContent);
    return template(data);
  }

  public async sendEmail(
    toAddress: string[],
    fromAddress: string,
    templateName: string,
    subject: string,
    data: any
  ): Promise<void> {
    const htmlBody = await this.compileTemplate(templateName, {
      ...data,
    });

    const sendEmailCommand = new SendEmailCommand({
      Destination: {
        ToAddresses: toAddress,
      },
      Message: {
        Body: {
          Html: {
            Charset: "UTF-8",
            Data: htmlBody,
          },
        },
        Subject: {
          Charset: "UTF-8",
          Data: subject,
        },
      },
      Source: fromAddress,
    });

    try {
      await this.sesClient.send(sendEmailCommand);
      console.info("Email sent successfully");
    } catch (error) {
      console.error("Error sending email:", error);
      throw error;
    }
  }
}

export default EmailService;

Pros of the solution

Demo !!

CD to the repository you cloned and run

  pnpm dev
  Deployed:
   Events
   API
   ApiEndpoint: https://o98egbtw09.execute-api.eu-west-1.amazonaws.com

Now call the API to send an email

send email via api

And here is the preview of the received email received email

Conclusion

Embedding email templates within your Lambda functions using AWS SES and SST provides a robust, scalable, and efficient email system. This methodology enhances performance, simplifies deployment, and improves maintainability by keeping your templates version-controlled and packaged with your code.

Call to Action

Ready to enhance your application’s email capabilities? Clone the sample repository and start building your production-ready email system with AWS SES and SST today!


Note:The code snippets provided are for educational purposes. Ensure you customize configurations such as AWS regions, email addresses, and template paths to suit your application’s requirements. Ensure that:

What’s next

The sample repository is simplified to focus on emailing features but It lacks many others like: