Search code examples
amazon-web-servicesamazon-ses

AMAZON SES: Sending email to users in HTML format


I want to change my current text email format to HTML format, so that I can send an email in nicely formatted way with headers, font values etc as shown in the screenshot below. Image showing email body with header font size etc

Currently I have text format for sending email using AWS.ses

exports.sendEmail = function(email, token, event, context) {
var  ses = new AWS.SES({
  region: process.env.SES_REGION
});
var eParams = {
    Destination: {
        ToAddresses: [email]
    },
    Message: {
        Body: {
            Text: {
              //template or environment variable
              Data: `Hello ${event.request.userAttributes.given_name},\n\nYour Device Validation Token is ${token}\nSimply copy this token and paste it into the device validation input field.`
            }
        },
        Subject: {
          //template or environment variable
            Data: "CWDS - CARES Device Validation Token"
        }
    },
    //environment variable
    Source: process.env.SOURCE_EMAIL
};

/* eslint-disable no-unused-vars */
ses.sendEmail(eParams, function(err, data){
  if(err) {
    logWrapper.logExceptOnTest("FAILURE SENDING EMAIL - Device Verify OTP");
    logWrapper.logExceptOnTest(event);
    logWrapper.logExceptOnTest(context);
    context.fail(err);
  }
  else {
    logWrapper.logExceptOnTest("Device Verify OTP sent");
    context.succeed(event);
  }
});
/* eslint-enable no-unused-vars */

}


Solution

  • It's fairly straight forward, just add the Html section of the Body node, i.e.:

    var eParams = {
        Destination: {
            ToAddresses: [email]
        },
        Message: {
            Body: {
                Text: {
                    Data: `Hello ${event.request.userAttributes.given_name},\n\nYour Device Validation Token is ${token}\nSimply copy this token and paste it into the device validation input field.`
                },
                Html: {
                    Data: `<html><head><title>Your Token</title><style>h1{color:#f00;}</style></head><body><h1>Hello ${event.request.userAttributes.given_name},</h1><div>Your Device Validation Token is ${token}<br/>Simply copy this token and paste it into the device validation input field.</div></body></html>`
                }
            },
            Subject: {
                //template or environment variable
                Data: "CWDS - CARES Device Validation Token"
            }
        },
        //environment variable
        Source: process.env.SOURCE_EMAIL
    };
    

    However... I generally split out the email template into html and text files, and use handlebars to inject data items. Here is an extract of one of my working solutions:

    contact/contact.js

    var AWS = require('aws-sdk');
    var ses = new AWS.SES();
    var fs = require('fs');
    var Handlebars = require('handlebars');
        
    module.exports.sendemail = (event, context, callback) =>
       var eventData = JSON.parse(event.body);
        
       fs.readFile("./contact/emailtemplate.html", function (err, emailHtmlTemplate) {
           if (err) {
               console.log("Unable to load HTML Template");
               throw err;
           }
           fs.readFile("./contact/emailtemplate.txt", function (err, emailTextTemplate) {
                if (err) {
                    console.log("Unable to load TEXT Template");
                    throw err;
                }
    
                // Prepare data for template placeholders
                var emailData = {
                    "given_name": event.request.userAttributes.given_name,
                    "token": token
                };
        
                // Inject data into templates
                var templateTitle = Handlebars.compile(process.env.EMAIL_TITLE);
                var titleText = templateTitle(emailData);
                console.log(titleText);
        
                emailData.title = titleText;
        
                var templateText = Handlebars.compile(emailTextTemplate.toString());
                var bodyText = templateText(emailData);
                console.log(bodyText);
    
                var templateHtml = Handlebars.compile(emailHtmlTemplate.toString());
                var bodyHtml = templateHtml(emailData);
                console.log(bodyHtml);
        
                // Prepare SES params
                
                var params = {
                    Destination: {
                        ToAddresses: [
                            process.env.EMAIL_TO
                        ]
                    },
                    Message: {
                        Body: {
                            Text: {
                                Data: bodyText,
                                Charset: 'UTF-8'
                            },
                            Html: {
                                Data: bodyHtml
                            },
                        },
                        Subject: {
                            Data: titleText,
                            Charset: 'UTF-8'
                        }
                    },
                    Source: process.env.EMAIL_FROM
                }
                console.log(JSON.stringify(params,null,4));
                
                // Send Email
                
                ses.sendEmail(params, function(err,data){
                    if(err) {
                        console.log(err,err.stack); // error
                        var response = {
                            statusCode: 500,
                            headers: {
                                "Access-Control-Allow-Origin" : "*",
                                "Access-Control-Allow-Credentials" : true
                            },
                            body: JSON.stringify({"message":"Error: Unable to Send Message"})
                        }
                        callback(null, response);
                    }
                    else  {
                        console.log(data); // success
        
                        var response = {
                            statusCode: 200,
                            headers: {
                                "Access-Control-Allow-Origin" : "*",
                                "Access-Control-Allow-Credentials" : true
                            },
                            body: JSON.stringify({"message":"Message Sent"})
                        }
                        callback(null, response);
                    }
                });
       
             }); //end of load text template
        }); //end of load html template
    };
    

    contact/emailtemplate.html

    <!DOCTYPE html>
    <html>
    <head>
        <title>Your New Token</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <style>
            h1 { color:#f00; }
        </style>
    </head>
    <body>
    <div class="emailwrapper">
        <div class="main">
            <div class="content">
                <h1>Hi {{given_name}}</h1>
                <div style="padding:20px;">
                    <div>your new token is {{token}}.</div>
                </div>
            </div>
        </div>
    </div>
    </body>
    </html>
    

    contact/emailtemplate.txt

    Hi {{given_name}}
    
    your new token is {{token}}.
    

    UPDATE 2023 (ES6 + AWS-SDK)

    Here is the ES6 version using the newer AWS SDK, and some other optimisations:

    import { SESClient, SendEmailCommand } from '@aws-sdk/client-ses';
    import fs from 'fs/promises';
    import Handlebars from 'handlebars';
    import util from 'util';
    
    const ses = new SESClient({ region: 'your-aws-region' }); // Replace 'your-aws-region' with the appropriate AWS region
    
    const readFileAsync = util.promisify(fs.readFile);
    
    export const sendemail = async (event, context, callback) => {
        try {
            const eventData = JSON.parse(event.body);
            const [emailHtmlTemplate, emailTextTemplate] = await Promise.all([
                readFileAsync("./contact/emailtemplate.html"),
                readFileAsync("./contact/emailtemplate.txt")
            ]);
    
            // Prepare data for template placeholders
            const emailData = {
                "given_name": event.request.userAttributes.given_name,
                "token": token // Where does 'token' come from? You need to define it.
            };
    
            // Inject data into templates
            const templateTitle = Handlebars.compile(process.env.EMAIL_TITLE);
            const titleText = templateTitle(emailData);
            console.log(titleText);
    
            emailData.title = titleText;
    
            const templateText = Handlebars.compile(emailTextTemplate.toString());
            const bodyText = templateText(emailData);
            console.log(bodyText);
    
            const templateHtml = Handlebars.compile(emailHtmlTemplate.toString());
            const bodyHtml = templateHtml(emailData);
            console.log(bodyHtml);
    
            // Prepare SES params
            const params = {
                Destination: {
                    ToAddresses: [process.env.EMAIL_TO]
                },
                Message: {
                    Body: {
                        Text: {
                            Data: bodyText,
                            Charset: 'UTF-8'
                        },
                        Html: {
                            Data: bodyHtml
                        },
                    },
                    Subject: {
                        Data: titleText,
                        Charset: 'UTF-8'
                    }
                },
                Source: process.env.EMAIL_FROM
            };
            console.log(JSON.stringify(params, null, 4));
    
            // Send Email
            const sendEmailCommand = new SendEmailCommand(params);
            await ses.send(sendEmailCommand);
    
            const response = {
                statusCode: 200,
                headers: {
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Credentials": true
                },
                body: JSON.stringify({ "message": "Message Sent" })
            };
            callback(null, response);
        } catch (error) {
            console.log(error, error.stack); // error
            const response = {
                statusCode: 500,
                headers: {
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Credentials": true
                },
                body: JSON.stringify({ "message": "Error: Unable to Send Message" })
            };
            callback(null, response);
        }
    };