Do you want to use the awesome Stripo service to create HTML email templates, and then send them with AWS SES and Lambda? Well, you are in the right place!
Using stripo.email we can create ourselves some awesome looking HTML email templates, and then using Serverless we can spin up an email service that lets us send those emails using AWS SES, invoked via a Lambda function.
yarn install
to install the dependencies.env.example
file to .env
and fill in the valuessls deploy
to deploy the service to AWSIf you already have an SES identity for our domain in the region you are deploying this too, it will fail to deploy because the domain already exists in SES.
If this is the case, you will need to simply comment out everything under the resources:
section in the serverless.yml
file, and then deploy the service. This will then create the Lambda function & using your existing SES domain (its essentially the same thing, we are just skipping the SES setup part!)
# The editable parts, either edit the below directly, or copy .env.example to .env and edit there! custom: IAM_PROFILE: ${env:IAM_PROFILE} # e.g. YourIAMCredentialsUser SERVICE_NAME: ${env:SERVICE_NAME} # e.g. send-email-ses MY_REGION: ${env:MY_REGION} # e.g us-east-1 EMAIL_DOMAIN: ${env:EMAIL_DOMAIN} # e.g yourdomain.com, must be registered in route53 # NOTE: Only fill out the below if you know what you're doing! # Make sure we include any handlebars .hbs files bundle: rawFileExtensions: - hbs service: ${self:custom.SERVICE_NAME} useDotenv: true provider: name: aws profile: ${self:custom.IAM_PROFILE} runtime: nodejs18.x region: ${self:custom.MY_REGION} iam: role: statements: - Effect: "Allow" Action: - ses:SendEmail Resource: "*" plugins: - serverless-bundle # TypeScript bundler functions: sendEmail: name: ${self:custom.SERVICE_NAME}-${sls:stage} environment: EMAIL_DOMAIN: ${self:custom.EMAIL_DOMAIN} REGION: ${self:custom.MY_REGION} handler: src/sendEmail.handler # Comment out all of the below if you already have an email domain registered in SES resources: Resources: SESIdentity: Type: AWS::SES::EmailIdentity Properties: DkimAttributes: SigningEnabled: true DkimSigningAttributes: NextSigningKeyLength: RSA_2048_BIT EmailIdentity: ${self:custom.EMAIL_DOMAIN} FeedbackAttributes: EmailForwardingEnabled: true MailFromAttributes: BehaviorOnMxFailure: USE_DEFAULT_VALUE MailFromDomain: email.${self:custom.EMAIL_DOMAIN} Route53SESIdendityVerificationRecord1: Type: AWS::Route53::RecordSet Properties: Name: !GetAtt SESIdentity.DkimDNSTokenName1 Comment: ${self:custom.EMAIL_DOMAIN}-SES-1 Type: CNAME HostedZoneName: ${self:custom.EMAIL_DOMAIN}. TTL: "900" ResourceRecords: - !GetAtt SESIdentity.DkimDNSTokenValue1 Route53SESIdendityVerificationRecord2: Type: AWS::Route53::RecordSet Properties: Name: !GetAtt SESIdentity.DkimDNSTokenName2 Comment: ${self:custom.EMAIL_DOMAIN}-SES-2 Type: CNAME HostedZoneName: ${self:custom.EMAIL_DOMAIN}. TTL: "900" ResourceRecords: - !GetAtt SESIdentity.DkimDNSTokenValue2 Route53SESIdendityVerificationRecord3: Type: AWS::Route53::RecordSet Properties: Name: !GetAtt SESIdentity.DkimDNSTokenName3 Comment: ${self:custom.EMAIL_DOMAIN}-SES-3 Type: CNAME HostedZoneName: ${self:custom.EMAIL_DOMAIN}. TTL: "900" ResourceRecords: - !GetAtt SESIdentity.DkimDNSTokenValue3 Route53SESMailFromMX: Type: AWS::Route53::RecordSet Properties: Name: email.${self:custom.EMAIL_DOMAIN} Comment: ${self:custom.EMAIL_DOMAIN}-SES-MX Type: MX HostedZoneName: ${self:custom.EMAIL_DOMAIN}. TTL: "900" ResourceRecords: - 10 feedback-smtp.${self:custom.MY_REGION}.amazonses.com Route53SESMailFromTXT: Type: AWS::Route53::RecordSet Properties: Name: email.${self:custom.EMAIL_DOMAIN} Comment: ${self:custom.EMAIL_DOMAIN}-SES-TXT Type: TXT HostedZoneName: ${self:custom.EMAIL_DOMAIN}. TTL: "900" ResourceRecords: - '"v=spf1 include:amazonses.com ~all"'
// DEPLOY: sls deploy -f sendEmail --verbose // LOGS: sls logs -f sendEmail -t // BOTH: sls deploy -f sendEmail --verbose && sls logs -f sendEmail -t import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses"; import Handlebars from "handlebars"; import template from "./template.hbs"; const { REGION, EMAIL_DOMAIN } = process.env; if (!EMAIL_DOMAIN) { throw new Error("EMAIL_DOMAIN is required"); } if (!REGION) { throw new Error("REGION is required"); } // Our expected inputs interface iEvent { name: string; email: string; subject: string; } export const handler = async (event: iEvent) => { try { const sesClient = new SESClient({ region: REGION }); const { name, email, subject } = event; // Compile HTML with Handlebars const compiled = Handlebars.compile(template); const htmlMessage = compiled({ username: name, useremail: email }); // Set our source and replyTo addresses const source = `hello@${EMAIL_DOMAIN}`; const replyTo = `contact@${EMAIL_DOMAIN}`; const command = new SendEmailCommand({ Source: source, Destination: { ToAddresses: [email], // Please note: if in Sandbox mode, this must be a verified email address }, Message: { Subject: { Data: subject }, Body: { Html: { Charset: "UTF-8", Data: htmlMessage, }, Text: { Charset: "UTF-8", Data: "Please view this email in a client that supports HTML", }, }, }, ReplyToAddresses: [replyTo], }); await sesClient.send(command); // You should have been sent the email by now!! } catch (error: any) { console.log("โ Error sending email:", error); } };
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <h1 style="color:red">{{username}}</h1> <p style="color:green">Welcome {{username}}, {{useremail}} to mywebsite.com</p>
๐ This HTML email has been kept as basic as possible for the purposes of this article/demo - but as you can see we can apply HTML formatting, and pass in variables to it (which get processed via the Handlebars
module in our lambda function).
So you can start from here from scratch and build out your own amazing HTML email templates!
First either make sure your SES account is out of Sandbox mode, or that the email address that you are sending too has been verified in your AWS console.
Then go to Lambda in the region you deployed this too, find the function called send-email-ses-dev
and click on it (or to whatever you changed the service name to)
Click on the Test tab, and create a new test event.
{ "name": "Name of User", "email": "yourverifiedemail@example.com", "subject": "This is a test email! I hope you like it! ๐ฅณ" }
Click Test, and you should receive the email! ๐
If you see any errors, read them carefully to see what went wrong and you can debug from there.
Well, I'm glad you asked! I also suddenly realized that halfway through this blog article, I might not have the rights to publish one of Stripo's HTML templates, so I will simply show you an example screenshot of what I managed to conjure up using their templates.
This is a design I will be implementing soon on my www.3dnames.co website once my next update is complete!
Variables used in this example template would be:
{ "name": "Name of User", "email": "user@example.com", "subject": "Your Sign Up Code", "code" : "123456", "website" : "www.3dnames.co" }