Search documentation

Find a page in the Andurel docs.

Email

Andurel v2 provides github.com/mbvlabs/andurel/pkg/email for transport-neutral messages, application-owned Templ templates, Tailwind-to-inline-style compilation, and separate transactional and marketing sender interfaces.

Sender interfaces

Consumers depend on capability-specific interfaces. The composition root selects EMAIL_PROVIDER and publishes these interfaces:

1type TransactionalSender interface {
2    SendTransactional(context.Context, TransactionalPayload) error
3}
4type MarketingSender interface {
5    SendMarketing(context.Context, MarketingPayload) error
6}

Generated apps default EMAIL_PROVIDER to mailpit for local capture. Wire a production transport in the composition root that implements the same interfaces.

Generate and compile email

1andurel generate email WelcomeEmail
2andurel sync email

That creates email/welcome_email.templ with a Transformer:

 1type WelcomeEmail struct {
 2}
 3
 4var _ Transformer = (*WelcomeEmail)(nil)
 5
 6func (e WelcomeEmail) ToHTML() (string, error) {
 7    var buf bytes.Buffer
 8    if err := e.render().Render(context.Background(), &buf); err != nil {
 9        return "", err
10    }
11    return buf.String(), nil
12}
13
14func (e WelcomeEmail) ToText() (string, error) {
15    html, err := e.ToHTML()
16    if err != nil {
17        return "", err
18    }
19    return HTMLToText(html)
20}
21
22templ (e WelcomeEmail) render() {
23    @baseLayout("Subject", "Pre-header text") {
24        // authored Tailwind markup
25    }
26}

Write utilities in css/email.css. andurel run, andurel sync views, andurel sync email, and andurel build compile templates automatically without changing the authored .templ files.

Send typed messages

Build email.TransactionalData, then call SendTransactional with an injected sender:

 1html, err := welcome.ToHTML()
 2if err != nil {
 3    return err
 4}
 5text, err := welcome.ToText()
 6if err != nil {
 7    return err
 8}
 9
10return email.SendTransactional(ctx, email.TransactionalData{
11    To:       user.Email,
12    From:     mailCfg.DefaultSenderSignature,
13    Subject:  "Welcome",
14    HTMLBody: html,
15    TextBody: text,
16}, sender)

Marketing messages require an unsubscribe URL. Validation, temporary, and permanent errors are distinguished so queue workers can choose retry behavior with email.IsRetryable / email.IsValidationError.

Mailpit locally

1andurel tool mailpit

Open the Mailpit UI (default http://127.0.0.1:8025) and confirm the message appears after the send call. Keep EMAIL_PROVIDER=mailpit in development .env so the Fx module publishes the Mailpit client as TransactionalSender / MarketingSender.

Delivery from the queue

Use the queue for delivery that should not hold open an HTTP request. Commit domain state and a River job together, then send from cmd/queue. See Queues.