Send email
gas/email implements gas.EmailProvider on AWS SES. Bodies can be literal
strings or rendered from templates held by a gas.TemplateProvider, which is
the same interface Serve HTML uses, so both surfaces can
share one store.
go get github.com/gasmod/gas/emailWiring
Section titled “Wiring”app := gas.NewApp( gas.WithServiceInstance[gas.ConfigProvider](cfg), gas.WithSingletonService[gas.Logger](gaslog.NewSlogLogger()),
// ses.New also asks for a gas.TemplateProvider, which is what // SendFromTemplate renders through. gas.WithServiceInstance[gas.TemplateProvider](templatememory.NewStore()), gas.WithSingletonService[gas.EmailProvider](emailses.New()),)If your container resolves a concrete store type rather than the interface, use
ses.NewWithCustomProvider[*mystore.Store]().
Sending
Section titled “Sending”type Service struct { email gas.EmailProvider}
func New(email gas.EmailProvider) *Service { return &Service{email: email} }
func (s *Service) confirmOrder(ctx context.Context, to string) error { return s.email.Send(ctx, &gas.Email{ To: []string{to}, Subject: "Order confirmed", HTMLBody: "<h1>Thank you</h1>", TextBody: "Thank you", ReplyTo: "support@example.com", })}From defaults to Email.FromEmail from config and can be overridden per
message. Cc, Bcc, ReplyTo, and arbitrary Headers are available.
Templated sends
Section titled “Templated sends”// Templates are fetched from the TemplateProvider, parsed with html/template// for HTML and text/template for the subject and text bodies. Any field left// empty falls back to the matching field on the embedded Email.func (s *Service) welcome(ctx context.Context, to, name string) error { return s.email.SendFromTemplate(ctx, &gas.TemplatedEmail{ SubjectTemplate: "welcome-subject", HTMLTemplate: "welcome-html", TextTemplate: "welcome-text", Data: map[string]string{"Name": name}, Email: gas.Email{To: []string{to}}, })}Subject and text bodies render with text/template; the HTML body renders with
html/template, so it escapes by default. Any template field left empty falls
back to the corresponding literal field on the embedded Email, which means you
can template the body and hard-code the subject.
Local development
Section titled “Local development”Point Email.Endpoint at LocalStack and nothing else changes:
EMAIL__REGION=us-east-1EMAIL__FROM_EMAIL=noreply@example.comEMAIL__ENDPOINT=http://localhost:4566Config
Section titled “Config”| Field | Description |
|---|---|
Email.Region |
AWS region (required) |
Email.FromEmail |
Default sender address (required) |
Email.Endpoint |
Custom endpoint, for LocalStack. Empty uses AWS |
Email.AccessKeyID |
Static access key. Empty uses the default credential chain |
Email.SecretAccessKey |
Static secret key |
Readiness
Section titled “Readiness”The service implements gas.ReadyReporter, returning email.ErrClosed once
Close has run so a Kubernetes readiness probe depools the pod during a drain.
gas.HealthReporter is deliberately not implemented: the SES client is
stateless, so there is no broken state a restart would clear.
Testing
Section titled “Testing”emailtest.MockEmail records every call and delegates to SendFn and
SendFromTemplateFn when set, so a signup flow can assert that exactly one
welcome email was sent without touching AWS. See
Test a service.