Help us shape the future of Go! Take the 2026 Go Developer Survey

mail

package
v0.24.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 29 Imported by: 1

Documentation

Overview

Package mail sends email. A Mailable describes one email (envelope and content); a Mailer renders it and hands the Message to a Transport: SMTP, Resend, log (development) or Array (tests).

type Welcome struct{ User models.User }

func (m Welcome) Envelope() mail.Envelope {
	return mail.Envelope{To: []string{m.User.Email}, Subject: "Welcome!"}
}
func (m Welcome) Content() mail.Content { return mail.View("mail/welcome", m) }

err := app.Mailer().Send(ctx, Welcome{User: u})

The app builds its Mailer from MAIL_* variables; Preview renders mailables in the browser during development.

See https://github.com/AlexGalhardo/vukem/blob/main/docs/subsystems.md#mail.

Example
package main

import (
	"context"
	"fmt"

	"github.com/AlexGalhardo/vukem/mail"
)

type Welcome struct{ Email string }

func (m Welcome) Envelope() mail.Envelope {
	return mail.Envelope{To: []string{m.Email}, Subject: "Welcome!"}
}

func (m Welcome) Content() mail.Content { return mail.Text("Thanks for signing up.") }

func main() {
	// Array keeps messages in memory (tests); apps configure SMTP or
	// Resend through MAIL_* variables.
	sent := mail.NewArray()
	m := mail.New(sent, "Shop <hello@shop.test>", nil)

	if err := m.Send(context.Background(), Welcome{Email: "ada@example.com"}); err != nil {
		fmt.Println(err)
		return
	}

	msgs := sent.SentTo("ada@example.com")
	for i := range msgs {
		fmt.Println(msgs[i].From, "|", msgs[i].Subject, "|", msgs[i].Text)
	}
}
Output:
"Shop" <hello@shop.test> | Welcome! | Thanks for signing up.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNoQueue = errors.New("mail: SendLater needs a queue (use app.Mailer or Mailer.SetEnqueue)")

ErrNoQueue is returned by SendLater on a mailer without an Enqueue.

View Source
var ErrNoRecipients = errors.New("mail: no recipients")

ErrNoRecipients is returned for messages without To, Cc or Bcc.

Functions

func Build

func Build(msg *Message) ([]byte, error)

Build renders msg as RFC 5322 bytes: multipart/mixed (attachments) over multipart/related (inline parts) over multipart/alternative (text, HTML). Bcc is never written as a header.

func Preview

func Preview(m *Mailer, mailables map[string]Mailable) http.Handler

Preview serves rendered mailables for development: the index lists names, "<name>" shows the HTML part and "<name>?text=1" the text part. Mount it only outside production:

if !app.Config.IsProduction() {
	r.Handle("/_mail/", http.StripPrefix("/_mail/", mail.Preview(mailer, map[string]mail.Mailable{...})))
}

Types

type Array

type Array struct {
	// contains filtered or unexported fields
}

Array keeps sent messages in memory (tests; vukemtest uses it).

func NewArray

func NewArray() *Array

NewArray returns an empty Array transport.

func (*Array) Reset

func (a *Array) Reset()

Reset forgets sent messages.

func (*Array) Send

func (a *Array) Send(_ context.Context, msg *Message) error

Send implements Transport.

func (*Array) Sent

func (a *Array) Sent() []Message

Sent returns a copy of the sent messages.

func (*Array) SentTo

func (a *Array) SentTo(addr string) []Message

SentTo returns the messages with addr among To, Cc or Bcc.

type Attachable added in v0.21.0

type Attachable interface {
	Attachments() []Attachment
}

Attachable is implemented by mailables that attach files.

type Attachment

type Attachment struct {
	Filename    string
	ContentType string
	Data        []byte
	Inline      bool
	ContentID   string
}

Attachment is a file sent with the message; Inline parts are referenced from HTML as cid:<ContentID>.

type Content

type Content struct {
	// contains filtered or unexported fields
}

Content is the body: a view (rendered by the Mailer) or literal parts.

func HTML

func HTML(htmlBody, text string) Content

HTML is literal HTML (and optional text) content.

func Text

func Text(text string) Content

Text is plain-text content.

func View

func View(name string, data any) Content

View renders the html/template page name with data; the plain-text part comes from the page "<name>_text" when it exists, else from the HTML.

type Enqueue added in v0.24.0

type Enqueue func(ctx context.Context, msg *Message, opts ...queue.Option) error

Enqueue stores a rendered message in a queue job that sends it later. The app's mailer (app.Mailer) enqueues on the app queue with the payload encrypted, so mail bodies (reset links, tokens) never sit in the jobs tables in clear text.

type Envelope

type Envelope struct {
	From    string
	To      []string
	Cc      []string
	Bcc     []string
	ReplyTo []string
	Subject string
	// Tags are provider metadata (Resend tags; X-Tag headers over SMTP).
	Tags map[string]string
}

Envelope holds addresses and headers. Addresses accept "Name <a@b.c>".

type Log

type Log struct {
	L *slog.Logger
}

Log writes messages to a logger instead of sending them (development). Bodies are logged at debug level only.

func (Log) Send

func (l Log) Send(ctx context.Context, msg *Message) error

Send implements Transport.

type Mailable

type Mailable interface {
	Envelope() Envelope
	Content() Content
}

Mailable is one email.

type Mailer

type Mailer struct {
	// contains filtered or unexported fields
}

Mailer renders mailables and sends them.

func New

func New(t Transport, from string, views web.Renderer) *Mailer

New returns a Mailer sending from the default from address through t. views renders View contents (the app's view engine); it may be nil when only literal contents are used.

func (*Mailer) Render

func (m *Mailer) Render(ctx context.Context, mailable Mailable) (*Message, error)

Render builds the Message without sending it (previews, tests).

func (*Mailer) Send

func (m *Mailer) Send(ctx context.Context, mailable Mailable) error

Send renders and delivers mailable.

func (*Mailer) SendLater added in v0.24.0

func (m *Mailer) SendLater(ctx context.Context, mailable Mailable, opts ...queue.Option) error

SendLater renders mailable now (so rendering errors reach the caller) and sends it from a queue job, retried with the queue's backoff. The message ID stays the same across retries, so providers that deduplicate by it send once. opts select the queue, delay or retries of this mail. With QUEUE_CONNECTION=sync the job runs during the call.

func (*Mailer) SetEnqueue added in v0.24.0

func (m *Mailer) SetEnqueue(fn Enqueue)

SetEnqueue sets how SendLater queues messages. Call it before the mailer is used; app.Mailer sets it.

func (*Mailer) Transport

func (m *Mailer) Transport() Transport

Transport returns the mailer's transport (tests read the Array).

type Message

type Message struct {
	// ID identifies the message (Message-ID, Resend idempotency key).
	ID          string
	From        string
	To          []string
	Cc          []string
	Bcc         []string
	ReplyTo     []string
	Subject     string
	HTML        string
	Text        string
	Attachments []Attachment
	Tags        map[string]string
}

Message is a rendered email ready for a Transport.

type Resend

type Resend struct {
	// contains filtered or unexported fields
}

Resend sends through the Resend HTTP API. The message ID is the Idempotency-Key, so retries never send twice.

func NewResend

func NewResend(cfg ResendConfig) *Resend

NewResend returns the Resend transport.

func (*Resend) Send

func (r *Resend) Send(ctx context.Context, msg *Message) error

Send implements Transport, retrying 429 and 5xx with backoff.

type ResendConfig

type ResendConfig struct {
	APIKey config.Secret
	// BaseURL defaults to https://api.resend.com (tests use a fake).
	BaseURL string
	// HTTP is the client (10s timeout by default).
	HTTP *http.Client
	// MaxRetries for 429 and 5xx responses (default 2).
	MaxRetries int
	// MaxBackoff caps waits between retries, including Retry-After (default 5s).
	MaxBackoff time.Duration
}

ResendConfig configures the Resend transport (https://resend.com).

type ResendError

type ResendError struct {
	Status  int
	Name    string
	Message string
}

ResendError is an error answered by the Resend API. It never contains the API key.

func (*ResendError) Error

func (e *ResendError) Error() string

func (*ResendError) Retryable

func (e *ResendError) Retryable() bool

Retryable reports whether the request may succeed later.

type SMTP

type SMTP struct {
	// contains filtered or unexported fields
}

SMTP sends through an SMTP server with STARTTLS (or implicit TLS on 465).

func NewSMTP

func NewSMTP(cfg SMTPConfig) *SMTP

NewSMTP returns the SMTP transport.

func (*SMTP) Send

func (s *SMTP) Send(ctx context.Context, msg *Message) error

Send implements Transport.

type SMTPConfig

type SMTPConfig struct {
	Host     string
	Port     int // 587 (STARTTLS, default) or 465 (implicit TLS)
	Username string
	Password config.Secret
	// InsecureSkipTLS sends without TLS (local catchers such as Mailpit
	// only); by default STARTTLS is required.
	InsecureSkipTLS bool
	// Timeout bounds dialing and the whole exchange (default 30s).
	Timeout time.Duration
	// TLSConfig overrides the TLS settings (tests).
	TLSConfig *tls.Config
}

SMTPConfig configures the SMTP transport.

type Transport

type Transport interface {
	Send(ctx context.Context, msg *Message) error
}

Transport delivers messages.

type WithAttachments deprecated

type WithAttachments = Attachable

WithAttachments is the pre-1.0 name of Attachable; With* names are reserved for options.

Deprecated: use Attachable.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL