Creating mail clients
NewMailClient()
Create a mail client based on current app settings:mailer := app.NewMailClient()
Sending emails
Basic email
import (
"net/mail"
"github.com/pocketbase/pocketbase/tools/mailer"
)
mailer := app.NewMailClient()
message := &mailer.Message{
From: mail.Address{
Name: "My App",
Address: "noreply@example.com",
},
To: []mail.Address{
{Address: "user@example.com"},
},
Subject: "Welcome to My App",
HTML: "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
}
if err := mailer.Send(message); err != nil {
log.Fatal(err)
}
Email with text alternative
message := &mailer.Message{
From: mail.Address{
Address: "noreply@example.com",
},
To: []mail.Address{
{Address: "user@example.com"},
},
Subject: "Welcome",
HTML: "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
Text: "Welcome! Thanks for signing up.",
}
mailer.Send(message)
Multiple recipients
message := &mailer.Message{
From: mail.Address{
Name: "Newsletter",
Address: "newsletter@example.com",
},
To: []mail.Address{
{Address: "user1@example.com"},
{Address: "user2@example.com"},
{Address: "user3@example.com"},
},
Cc: []mail.Address{
{Address: "manager@example.com"},
},
Bcc: []mail.Address{
{Address: "admin@example.com"},
},
Subject: "Monthly Newsletter",
HTML: "<h1>Newsletter content</h1>",
}
mailer.Send(message)
Email attachments
Add file attachments
import "os"
file, err := os.Open("invoice.pdf")
if err != nil {
log.Fatal(err)
}
defer file.Close()
message := &mailer.Message{
From: mail.Address{Address: "billing@example.com"},
To: []mail.Address{{Address: "customer@example.com"}},
Subject: "Your Invoice",
HTML: "<p>Please find your invoice attached.</p>",
Attachments: map[string]io.Reader{
"invoice.pdf": file,
},
}
mailer.Send(message)
Multiple attachments
file1, _ := os.Open("document1.pdf")
defer file1.Close()
file2, _ := os.Open("document2.pdf")
defer file2.Close()
message := &mailer.Message{
From: mail.Address{Address: "noreply@example.com"},
To: []mail.Address{{Address: "user@example.com"}},
Subject: "Your Documents",
HTML: "<p>Your requested documents are attached.</p>",
Attachments: map[string]io.Reader{
"document1.pdf": file1,
"document2.pdf": file2,
},
}
mailer.Send(message)
Inline attachments (embedded images)
image, _ := os.Open("logo.png")
defer image.Close()
message := &mailer.Message{
From: mail.Address{Address: "noreply@example.com"},
To: []mail.Address{{Address: "user@example.com"}},
Subject: "Welcome Email",
HTML: `<h1>Welcome!</h1><img src="cid:logo.png" alt="Logo">`,
InlineAttachments: map[string]io.Reader{
"logo.png": image,
},
}
mailer.Send(message)
Inline attachments can be referenced in HTML using
cid:filename where filename matches the key in InlineAttachments.Custom headers
Add custom email headers:message := &mailer.Message{
From: mail.Address{Address: "noreply@example.com"},
To: []mail.Address{{Address: "user@example.com"}},
Subject: "Test Email",
HTML: "<p>Test content</p>",
Headers: map[string]string{
"X-Custom-Header": "custom-value",
"Reply-To": "support@example.com",
"X-Priority": "1",
},
}
mailer.Send(message)
Email templates
Using Go templates
import "html/template"
type EmailData struct {
Name string
Token string
URL string
}
tmpl := template.Must(template.New("welcome").Parse(`
<!DOCTYPE html>
<html>
<body>
<h1>Welcome {{.Name}}!</h1>
<p>Please verify your email by clicking the link below:</p>
<a href="{{.URL}}/verify?token={{.Token}}">Verify Email</a>
</body>
</html>
`))
var buf bytes.Buffer
err := tmpl.Execute(&buf, EmailData{
Name: "John Doe",
Token: "verification-token",
URL: "https://example.com",
})
if err != nil {
log.Fatal(err)
}
message := &mailer.Message{
From: mail.Address{Address: "noreply@example.com"},
To: []mail.Address{{Address: "user@example.com"}},
Subject: "Verify Your Email",
HTML: buf.String(),
}
mailer.Send(message)
Using PocketBase templates
import "github.com/pocketbase/pocketbase/tools/template"
registry := template.NewRegistry()
html, err := registry.LoadFiles(
"templates/email/welcome.html",
).Render(map[string]any{
"name": "John Doe",
})
if err != nil {
log.Fatal(err)
}
message := &mailer.Message{
From: mail.Address{Address: "noreply@example.com"},
To: []mail.Address{{Address: "user@example.com"}},
Subject: "Welcome",
HTML: html,
}
mailer.Send(message)
Intercepting emails
OnMailerSend hook
Intercept all outgoing emails:app.OnMailerSend().BindFunc(func(e *core.MailerEvent) error {
log.Printf("Sending email to: %v", e.Message.To)
log.Printf("Subject: %s", e.Message.Subject)
// Continue sending
return e.Next()
})
Modify emails before sending
app.OnMailerSend().BindFunc(func(e *core.MailerEvent) error {
// Add a custom header to all emails
if e.Message.Headers == nil {
e.Message.Headers = make(map[string]string)
}
e.Message.Headers["X-App-Version"] = "1.0.0"
// Add BCC to all emails
e.Message.Bcc = append(e.Message.Bcc, mail.Address{
Address: "archive@example.com",
})
return e.Next()
})
Block emails in development
app.OnMailerSend().BindFunc(func(e *core.MailerEvent) error {
if e.App.IsDev() {
log.Printf("[DEV] Email blocked: %s", e.Message.Subject)
log.Printf("[DEV] To: %v", e.Message.To)
return nil // Don't call e.Next() to prevent sending
}
return e.Next()
})
Auth-related emails
OnMailerRecordVerificationSend
Intercept verification emails:app.OnMailerRecordVerificationSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
// Customize verification email
e.Message.Subject = "Please verify your email address"
// Access the record
log.Printf("Sending verification to: %s", e.Record.Email())
return e.Next()
})
OnMailerRecordPasswordResetSend
Intercept password reset emails:app.OnMailerRecordPasswordResetSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
e.Message.Subject = "Reset your password"
return e.Next()
})
OnMailerRecordEmailChangeSend
Intercept email change confirmation emails:app.OnMailerRecordEmailChangeSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
log.Printf("Email change requested for user: %s", e.Record.Id)
return e.Next()
})
OnMailerRecordAuthAlertSend
Intercept auth alert emails (new login notifications):app.OnMailerRecordAuthAlertSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
e.Message.Subject = "Security Alert: New Login Detected"
return e.Next()
})
Sending custom emails from hooks
Welcome email on user registration
app.OnRecordAfterCreateSuccess("users").BindFunc(func(e *core.RecordEvent) error {
mailer := e.App.NewMailClient()
message := &mailer.Message{
From: mail.Address{
Name: "My App",
Address: "noreply@example.com",
},
To: []mail.Address{
{Address: e.Record.Email()},
},
Subject: "Welcome to My App!",
HTML: fmt.Sprintf(
"<h1>Welcome %s!</h1><p>Thanks for joining.</p>",
e.Record.GetString("name"),
),
}
if err := mailer.Send(message); err != nil {
// Log error but don't fail the request
e.App.Logger().Error("Failed to send welcome email", "error", err)
}
return e.Next()
})
Notification on record creation
app.OnRecordAfterCreateSuccess("posts").BindFunc(func(e *core.RecordEvent) error {
// Get all users who should be notified
users, err := e.App.FindRecordsByFilter(
"users",
"notifications = true",
"",
100,
0,
)
if err != nil {
return err
}
mailer := e.App.NewMailClient()
for _, user := range users {
message := &mailer.Message{
From: mail.Address{Address: "noreply@example.com"},
To: []mail.Address{{Address: user.Email()}},
Subject: "New Post Published",
HTML: fmt.Sprintf(
"<p>A new post '%s' has been published.</p>",
e.Record.GetString("title"),
),
}
if err := mailer.Send(message); err != nil {
e.App.Logger().Error("Failed to send notification", "error", err)
}
}
return e.Next()
})
HTML to text conversion
PocketBase can automatically convert HTML to plain text:import "github.com/pocketbase/pocketbase/tools/mailer"
html := "<h1>Hello</h1><p>This is a <strong>test</strong></p>"
text := mailer.StripHTML(html)
// Result: "Hello\nThis is a test"
message := &mailer.Message{
From: mail.Address{Address: "noreply@example.com"},
To: []mail.Address{{Address: "user@example.com"}},
Subject: "Test",
HTML: html,
Text: text, // Fallback for email clients that don't support HTML
}
Message type signature
type Message struct {
From mail.Address // Sender address
To []mail.Address // Recipient addresses
Bcc []mail.Address // Blind carbon copy addresses
Cc []mail.Address // Carbon copy addresses
Subject string // Email subject
HTML string // HTML content
Text string // Plain text content
Headers map[string]string // Custom headers
Attachments map[string]io.Reader // File attachments
InlineAttachments map[string]io.Reader // Inline/embedded attachments
}
Best practices
Always provide text alternative
message := &mailer.Message{
// ... other fields
HTML: htmlContent,
Text: mailer.StripHTML(htmlContent),
}
Handle errors gracefully
if err := mailer.Send(message); err != nil {
// Log error but don't fail the request
app.Logger().Error("Failed to send email", "error", err)
// Optionally queue for retry
}
Use templates for consistency
// Define reusable email templates
type EmailTemplates struct {
welcome *template.Template
verification *template.Template
passwordReset *template.Template
}
// Load once at startup
templates := &EmailTemplates{
welcome: template.Must(template.ParseFiles("templates/welcome.html")),
// ...
}
Respect email preferences
user, _ := app.FindRecordById("users", userId)
if user.GetBool("email_notifications") {
mailer.Send(message)
}