Snowball
Snowball

Reputation: 1512

How to create message body for Amazon SES sendRawEmail

I am trying to customize the Headers while using the AWS Node.js SDK.

It seems that the sendEmail API endpoint and accompanying SDK method doesn't support customizing Headers.

So the appropiate method to use would be sendRawEmail.

Unfortunately I couldn't find good information or a package that actually helps composing the raw email. Meaning sending both plain text and html formats and creating the message body and headers in accordance to RFC 2822 and RFC 5322.

How would a simple method that composes the raw body written in JavaScript look like?

Upvotes: 3

Views: 14755

Answers (2)

Snowball
Snowball

Reputation: 1512

Here is a barebones function I came up with, which has only the AWS Javascript SDK as a dependency. One thing I did wrong in the beginning was Base 64 encoding the RawMessage.Data, but the AWS SDK already takes care of that.

The empty lines \n are important as well.

const sendRawEmail = async () => {

    // Set up from_name, from_email, to, subject, message_id, plain_text, html and configuration_set variables from database or manually

    var date = new Date();

    var boundary = `----=_Part${ Math.random().toString().substr( 2 ) }`;

    var rawMessage = [
        `From: "${ from_name }" <${ from_email }>`, // Can be just the email as well without <>
        `To: ${ to }`,
        `Subject: ${ subject }`,
        `MIME-Version: 1.0`,
        `Message-ID: <${ message_id }@eu-west-1.amazonses.com>`, // Will be replaced by SES
        `Date: ${ formatDate( date ) }`, // Will be replaced by SES
        `Return-Path: <${ message_id }@eu-west-1.amazonses.com>`, // Will be replaced by SES
        `Content-Type: multipart/alternative; boundary="${ boundary }"`, // For sending both plaintext & html content
        // ... you can add more headers here as decribed in https://docs.aws.amazon.com/ses/latest/DeveloperGuide/header-fields.html
        `\n`,
        `--${ boundary }`,
        `Content-Type: text/plain; charset=UTF-8`,
        `Content-Transfer-Encoding: 7bit`,
        `\n`,
        plain_text,
        `--${ boundary }`,
        `Content-Type: text/html; charset=UTF-8`,
        `Content-Transfer-Encoding: 7bit`,
        `\n`,
        html,
        `\n`,
        `--${ boundary }--`
    ]

    // Send the actual email
    await ses.sendRawEmail( {
        Destinations: [
            to
        ],
        RawMessage: {
            Data: rawMessage.join( '\n' )
        },
        Source: from_email, // Must be verified within AWS SES
        ConfigurationSetName: configuration_set, // optional AWS SES configuration set for open & click tracking
        Tags: [
            // ... optional email tags
        ]

    } ).promise();

}

These are the utility methods for formatting the Date Header. You could use as well simply use the moment library like moment().format('ddd, DD MMM YYYY HH:MM:SS ZZ'); but it doesn't really matter as the Date Header gets overwritten by AWS SES anyway.

// Outputs timezone offset in format ZZ
const getOffset = ( date ) => {

    var offset          = - date.getTimezoneOffset();
    var offsetHours     = Math.abs( Math.floor( offset / 60 ) );
    var offsetMinutes   = Math.abs( offset ) - offsetHours * 60;

    var offsetSign      = ( offset > 0 ) ? '+' : '-';

    return offsetSign + ( '0' + offsetHours ).slice( -2 ) + ( '0' + offsetMinutes ).slice( -2 );

}

// Outputs two digit inputs with leading zero
const leadingZero = ( input ) => ( '0' + input ).slice( -2 );

// Formats date in ddd, DD MMM YYYY HH:MM:SS ZZ
const formatDate = ( date ) => {

    var weekdays = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ];

    var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];

    var weekday = weekdays[ date.getDay() ];

    var day = leadingZero( date.getDate() );

    var month = months[ date.getMonth() ];

    var year = date.getFullYear();

    var hour = leadingZero( date.getHours() );

    var minute = leadingZero( date.getMinutes() );

    var second = leadingZero( date.getSeconds() );

    var offset = getOffset( date );

    return `${ weekday }, ${ day } ${ month } ${ year } ${ hour }:${ minute }:${ second } ${ offset }`;

}

Upvotes: 8

Alexey
Alexey

Reputation: 3484

You should have a look at another npm package responsible for composing email messages with headers, like for example nodemailer. After a couple of minutes of googling I found an example with setting up headers and emailing via SES here, they are using mailcomposer, which was a predecessor of nodemailer

UPDATE: Seems that nodemailer no longer exposes build method, but there is another package from them nodemailer-ses-transport, which seems to do exactly what you need, customizing headers and sending via SES.

Upvotes: 2

Related Questions