Documentation
¶
Overview ¶
Package mailer provides a universal email sending interface with template rendering.
The package separates email sending (via providers) from template rendering, allowing easy swapping of email providers while keeping the same template system.
Architecture ¶
The package consists of three main components:
- Sender: Interface that email providers implement
- Renderer: Converts markdown templates with YAML frontmatter to HTML
- Mailer: High-level client combining Sender and Renderer
Usage ¶
Basic usage with the built-in Resend provider:
import (
"context"
"os"
"github.com/dmitrymomot/forge/pkg/mailer"
"github.com/dmitrymomot/forge/pkg/mailer/resend"
)
func main() {
ctx := context.Background()
// Create the provider
sender := resend.New(resend.Config{
APIKey: os.Getenv("RESEND_API_KEY"),
SenderEmail: "team@example.com",
SenderName: "Team",
})
// Create the renderer with embedded templates
renderer := mailer.NewRenderer(emails.FS, mailer.RendererConfig{})
// Create the mailer
m := mailer.New(sender, renderer, mailer.Config{
FallbackSubject: "Notification",
DefaultLayout: "base.html",
})
// Send templated email
err := m.Send(ctx, mailer.SendParams{
To: "user@example.com",
Template: "welcome.md",
Data: map[string]any{"Name": "John"},
})
if err != nil {
panic(err)
}
}
Templates ¶
Templates are markdown files with optional YAML frontmatter:
---
Subject: Welcome {{.Name}}!
---
# Welcome
Hello {{.Name}}, welcome to our service!
[!button|Get Started]({{.URL}})
Subject fields support Go template syntax ({{.Variable}}) for dynamic subjects.
Sending Emails ¶
Mailer provides two methods for sending emails:
- Send: Renders a template and sends the email
- SendRaw: Sends a pre-built Email without rendering
SendParams supports optional overrides for subject, layout, sender, reply-to, CC, BCC, and attachments.
Email Tags ¶
The Email type supports provider-specific tags for categorization:
email := &mailer.Email{
To: []string{"user@example.com"},
Subject: "Welcome",
HTML: "<p>Hello!</p>",
Tags: mailer.SimpleTags("welcome", "onboarding"),
}
Custom Providers ¶
Implement the Sender interface to add support for other email providers:
type MySender struct{}
func (s *MySender) Send(ctx context.Context, email *mailer.Email) error {
// Send email using your provider's API
return nil
}
// Use with mailer
m := mailer.New(&MySender{}, renderer, cfg)
Background Jobs ¶
Users create their own typed tasks for background email delivery:
type SendWelcomeTask struct {
mailer *mailer.Mailer
}
type WelcomePayload struct {
Email string
Name string
}
func (t *SendWelcomeTask) Name() string { return "send_welcome" }
func (t *SendWelcomeTask) Handle(ctx context.Context, p WelcomePayload) error {
return t.mailer.Send(ctx, mailer.SendParams{
To: p.Email,
Template: "welcome.md",
Data: p,
})
}
Errors ¶
The package defines several error variables for specific failure cases:
- ErrNoRecipient: No recipient specified
- ErrNoSubject: No subject provided
- ErrNoContent: No HTML content provided
- ErrTemplateNotFound: Template file not found
- ErrLayoutNotFound: Layout file not found
- ErrRenderFailed: Template rendering failed
- ErrSendFailed: Email sending failed
- ErrInvalidFrontmatter: Invalid YAML frontmatter
Index ¶
- Variables
- func NewButtonExtension() goldmark.Extender
- func NewButtonParser() parser.InlineParser
- func NewButtonRenderer(opts ...html.Option) renderer.NodeRenderer
- func Recipient(name, email string) string
- type Attachment
- type ButtonExtension
- type ButtonNode
- type Config
- type Email
- type Mailer
- type RenderResult
- type Renderer
- type RendererConfig
- type SendParams
- type Sender
- type Tags
- type Template
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNoRecipient indicates no recipient was specified. ErrNoRecipient = errors.New("email must have at least one recipient") // ErrNoSubject indicates no subject was provided. ErrNoSubject = errors.New("email must have a subject") // ErrNoContent indicates no HTML content was provided. ErrNoContent = errors.New("email must have HTML content") // ErrTemplateNotFound indicates the template file was not found. ErrTemplateNotFound = errors.New("template not found") // ErrLayoutNotFound indicates the layout file was not found. ErrLayoutNotFound = errors.New("layout not found") // ErrRenderFailed indicates template rendering failed. ErrRenderFailed = errors.New("failed to render template") // ErrSendFailed indicates email sending failed. ErrSendFailed = errors.New("failed to send email") // ErrInvalidFrontmatter indicates invalid YAML frontmatter. ErrInvalidFrontmatter = errors.New("invalid frontmatter") )
var KindButton = ast.NewNodeKind("Button")
KindButton is the node kind for ButtonNode.
Functions ¶
func NewButtonExtension ¶
NewButtonExtension creates a new button extension for goldmark.
func NewButtonParser ¶
func NewButtonParser() parser.InlineParser
NewButtonParser creates a new button inline parser.
func NewButtonRenderer ¶
func NewButtonRenderer(opts ...html.Option) renderer.NodeRenderer
NewButtonRenderer creates a new button node renderer.
Types ¶
type Attachment ¶
type Attachment struct {
Filename string // Display name for the attachment
ContentType string // MIME type (e.g., "application/pdf")
ContentID string // Optional Content-ID for inline attachments
Content []byte // Raw file content
}
Attachment represents an email attachment.
type ButtonExtension ¶
type ButtonExtension struct{}
ButtonExtension is a goldmark extension for button links.
func (*ButtonExtension) Extend ¶
func (e *ButtonExtension) Extend(m goldmark.Markdown)
type ButtonNode ¶
type ButtonNode struct {
ast.BaseInline
URL []byte
Label []byte
}
ButtonNode represents a button link in the AST.
func (*ButtonNode) Dump ¶
func (n *ButtonNode) Dump(source []byte, level int)
func (*ButtonNode) Kind ¶
func (n *ButtonNode) Kind() ast.NodeKind
type Config ¶
type Config struct {
FallbackSubject string `env:"FALLBACK_SUBJECT" envDefault:"Notification"`
DefaultLayout string `env:"DEFAULT_LAYOUT" envDefault:"base.html"`
}
Config holds mailer configuration. Embed this in your app config for env parsing with caarlos0/env.
type Email ¶
type Email struct {
Headers map[string]string // Custom headers
Tags Tags // Provider-specific tags/categories
Subject string // Email subject
HTML string // HTML body content
Text string // Plain text alternative
From string // Override default sender (if provider allows)
ReplyTo string // Reply-to address
To []string // Recipients (at least one required)
CC []string // Carbon copy recipients
BCC []string // Blind carbon copy recipients
Attachments []Attachment // File attachments
}
Email represents a fully-prepared email message ready for sending.
type Mailer ¶
type Mailer struct {
// contains filtered or unexported fields
}
Mailer provides high-level email sending with template rendering.
type RenderResult ¶
type RenderResult struct {
Metadata map[string]any
HTML string
Text string // Plain text from processed markdown (before HTML conversion)
}
RenderResult contains the rendered HTML, plain text, and extracted metadata.
type Renderer ¶
type Renderer struct {
// contains filtered or unexported fields
}
Renderer converts markdown templates with YAML frontmatter to HTML.
func NewRenderer ¶
func NewRenderer(filesystem fs.FS, cfg RendererConfig) *Renderer
NewRenderer creates a new renderer with the given config.
type RendererConfig ¶
type RendererConfig struct {
TemplateDir string `env:"TEMPLATE_DIR" envDefault:"."`
LayoutDir string `env:"LAYOUT_DIR" envDefault:"layouts"`
}
RendererConfig configures the renderer.
type SendParams ¶
type SendParams struct {
To string // Single recipient (most common case)
Template string // Template filename (e.g., "welcome.md")
Data any // Template data
// Optional overrides
Subject string // Override template subject
Layout string // Override default layout
From string // Override default sender
ReplyTo string // Reply-to address
CC []string // Carbon copy
BCC []string // Blind carbon copy
Attachments []Attachment // File attachments
}
SendParams contains parameters for sending a templated email.
type Sender ¶
type Sender interface {
// Send delivers an email message.
// The Email must have To, Subject, and HTML already set.
// Returns an error if delivery fails.
Send(ctx context.Context, email *Email) error
}
Sender defines the minimal interface that email providers must implement. It accepts a fully-prepared Email and handles the actual delivery.
type Tags ¶
Tags represents email tags/categories that can be either presence-only (using struct{}{}) or key-value pairs (using string values). This abstraction works across different email providers:
- Postmark: uses only tag names
- Resend: uses name-value pairs (presence-only tags become name="true")
func SimpleTags ¶
SimpleTags creates presence-only tags from a list of tag names. These are converted to appropriate format by each provider adapter.
type Template ¶
Template represents an email template with metadata and body.
func ParseTemplate ¶
ParseTemplate parses a template file content and extracts front matter metadata and markdown body.