In this email we are going to send two types of emails. One, using AWS SES template and the other one is, using a Golang template package.


Just bear in mind, your sender email must be a verified identity in AWS SES console! Also the template should be created like below.


{
"Template": {
"TemplateName": "api-credentials-text",
"SubjectPart": "API credentials!",
"TextPart": "Hello!,\r\nID: {{id}}\r\nSecret: {{secret}}\r\nRegards"
}
}

Files


main.go


package main

import (
"aws/aws"
"aws/template"
"context"
"log"
)

func main() {
ctx := context.Background()
noreply := "noreply@example.com"
to := "receiver@example.com"
templateData := map[string]string{"id": "some-id", "secret": "some-secret"}

// Initialise AWS config.
cfg, err := aws.NewConfig(ctx)
if err != nil {
log.Fatalln(err)
}

// Initialise SES client.
ses := aws.NewSES(cfg, noreply)

// Send templated email from AWS.
err = ses.SendTemplatedEmail(ctx, aws.SESSendTemplatedEmailArgs{
TOs: []string{to},
TemplateName: "api-credentials-text",
TemplateData: templateData,
})
if err != nil {
log.Fatalln(err)
}

// Parse Go template.
tmp, err := template.Parse("template/ses/api_credentials.txt", templateData)
if err != nil {
log.Fatalln(err)
}

// Send email from Go.
err = ses.SendEmail(ctx, aws.SESSendEmailArgs{
TOs: []string{to},
Subject: "API credentials!",
TextBody: tmp.String(),
})
}

aws/config.go


package aws

import (
"context"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
)

func NewConfig(ctx context.Context) (aws.Config, error) {
return config.LoadDefaultConfig(ctx)
}

aws/args.go


package aws

type SESSendTemplatedEmailArgs struct {
TOs []string
CCs []string
TemplateName string
TemplateData map[string]string
}

type SESSendEmailArgs struct {
TOs []string
CCs []string
Subject string
HtmlBody string
TextBody string
}

aws/ses.go


package aws

import (
"context"
"encoding/json"
"errors"
"fmt"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ses"
"github.com/aws/aws-sdk-go-v2/service/ses/types"
)

type SES struct {
noReply string
client *ses.Client
}

func NewSES(cfg aws.Config, noReply string) SES {
return SES{
noReply: noReply,
client: ses.NewFromConfig(cfg),
}
}

func (s SES) SendTemplatedEmail(ctx context.Context, args SESSendTemplatedEmailArgs) error {
if len(args.TOs) == 0 {
return errors.New("recipient email is missing")
}

if args.TemplateName == "" {
return errors.New("template name is missing")
}

var data string

if len(args.TemplateData) != 0 {
val, err := json.Marshal(args.TemplateData)
if err != nil {
return fmt.Errorf("unable to marshal template data: %w", err)
}
data = string(val)
}

_, err := s.client.SendTemplatedEmail(ctx, &ses.SendTemplatedEmailInput{
Source: aws.String(s.noReply),
Destination: &types.Destination{
ToAddresses: args.TOs,
CcAddresses: args.CCs,
},
Template: &args.TemplateName,
TemplateData: &data,
})
if err != nil {
return fmt.Errorf("unable to send email: %w", err)
}

return nil
}

func (s SES) SendEmail(ctx context.Context, args SESSendEmailArgs) error {
if len(args.TOs) == 0 {
return errors.New("recipient email is missing")
}

if args.HtmlBody == "" && args.TextBody == "" {
return errors.New("body is missing")
}

var body types.Body
if args.HtmlBody != "" {
body.Html = &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(args.HtmlBody),
}
}
if args.TextBody != "" {
body.Text = &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(args.TextBody),
}
}

_, err := s.client.SendEmail(ctx, &ses.SendEmailInput{
Source: aws.String(s.noReply),
Destination: &types.Destination{
ToAddresses: args.TOs,
CcAddresses: args.CCs,
},
Message: &types.Message{
Subject: &types.Content{
Charset: aws.String("UTF-8"),
Data: aws.String(args.Subject),
},
Body: &body,
},
})
if err != nil {
return fmt.Errorf("unable to send email: %w", err)
}

return nil
}

template/template.go


package template

import (
"bytes"
"text/template"
)

func Parse(file string, vars map[string]string) (*bytes.Buffer, error) {
tmp, err := template.ParseFiles(file)
if err != nil {
return nil, err
}

var res bytes.Buffer
if err := tmp.Execute(&res, vars); err != nil {
return nil, err
}

return &res, nil
}

template/ses/api_credentials.txt


Hello,

ID: {{.id}}
Secret: {{.secret}}

Regards