Send emails with Velocity's driver-based mail system supporting SMTP, Mailgun, SES, and more.
Velocity provides a powerful, driver-based email system that supports multiple mail services with a unified, fluent API.
Quick Start
.env file. No setup required!import "github.com/velocitykode/velocity/pkg/mail"
func main() {
// Mail auto-initializes from .env
mail.NewMessage().
To("user@example.com").
Subject("Welcome to Velocity").
Body("Thanks for signing up!").
Send()
}import "github.com/velocitykode/velocity/pkg/mail"
func main() {
html := `
<h1>Welcome to Velocity</h1>
<p>Thanks for signing up!</p>
`
mail.NewMessage().
To("user@example.com").
Subject("Welcome").
HTMLBody(html).
TextBody("Thanks for signing up!").
Send()
}import "github.com/velocitykode/velocity/pkg/mail"
func main() {
mail.NewMessage().
To("user@example.com").
Subject("Your Invoice").
Body("Please find your invoice attached.").
AttachFile("/path/to/invoice.pdf").
Send()
}Configuration
Configure email sending through environment variables in your .env file:
Log Driver (Development)
The log driver writes email details to the log instead of sending them - perfect for development:
MAIL_DRIVER=log
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="My Application"Local SMTP Driver
Send emails via local SMTP server or sendmail:
MAIL_DRIVER=local
MAIL_HOST=localhost
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls # Options: tls, ssl, none
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="My Application"
# Or use sendmail instead of SMTP
MAIL_SENDMAIL_PATH=/usr/sbin/sendmailPostmark Driver
Send transactional emails via Postmark:
MAIL_DRIVER=postmark
POSTMARK_TOKEN=your-server-token
POSTMARK_MESSAGE_STREAM=outbound # Options: outbound, broadcast
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="My Application"Mailgun Driver
Send emails via Mailgun API:
MAIL_DRIVER=mailgun
MAILGUN_DOMAIN=mg.yourdomain.com
MAILGUN_SECRET=your-api-key
MAILGUN_ENDPOINT=https://api.mailgun.net/v3 # Optional, defaults to US region
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="My Application"Building Messages
Fluent API
The Message type provides a fluent, chainable API:
import "github.com/velocitykode/velocity/pkg/mail"
msg := mail.NewMessage().
From("sender@example.com", "Sender Name").
To("recipient@example.com", "Recipient Name").
CC("manager@example.com").
BCC("admin@example.com").
ReplyTo("support@example.com").
Subject("Important Update").
TextBody("Plain text content").
HTMLBody("<h1>HTML content</h1>").
Priority(mail.HighPriority)
msg.Send()Multiple Recipients
mail.NewMessage().
To("user1@example.com").
To("user2@example.com").
To("user3@example.com", "User Three").
Subject("Newsletter").
Body("Monthly newsletter content").
Send()Custom Headers
mail.NewMessage().
To("user@example.com").
Subject("Custom Headers").
Body("Content").
Header("X-Custom-ID", "12345").
Header("X-Campaign", "summer-sale").
Send()Templates
Render HTML templates for emails:
import (
"github.com/velocitykode/velocity/pkg/mail"
)
// Create template file: templates/welcome.html
data := map[string]interface{}{
"Name": "John Doe",
"URL": "https://example.com/verify",
}
mail.NewMessage().
To("user@example.com").
Subject("Welcome!").
Template("welcome", data).
Send()Example template (templates/welcome.html):
<!DOCTYPE html>
<html>
<head>
<title>Welcome</title>
</head>
<body>
<h1>Welcome, {{.Name}}!</h1>
<p>Click here to verify your account:</p>
<a href="{{.URL}}">Verify Account</a>
</body>
</html>Attachments
From File System
mail.NewMessage().
To("user@example.com").
Subject("Documents").
Body("Please find the documents attached.").
AttachFile("/path/to/document.pdf").
AttachFile("/path/to/image.jpg").
Send()From Memory
import "github.com/velocitykode/velocity/pkg/mail"
// Generate PDF or other data
pdfData := generateInvoicePDF()
mail.NewMessage().
To("user@example.com").
Subject("Your Invoice").
Body("Invoice attached.").
AttachData(pdfData, "invoice.pdf", "application/pdf").
Send()Advanced Usage
Using Context
For timeout control and cancellation:
import (
"context"
"time"
"github.com/velocitykode/velocity/pkg/mail"
)
func sendEmail() error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
msg := mail.NewMessage().
To("user@example.com").
Subject("Hello").
Body("Content")
return mail.Send(ctx, msg)
}Priority Levels
import "github.com/velocitykode/velocity/pkg/mail"
// High priority (urgent)
mail.NewMessage().
To("admin@example.com").
Subject("URGENT: Server Down").
Body("Server is down!").
Priority(mail.HighPriority).
Send()
// Normal priority (default)
mail.NewMessage().
To("user@example.com").
Subject("Update").
Body("Regular update").
Priority(mail.NormalPriority).
Send()
// Low priority
mail.NewMessage().
To("user@example.com").
Subject("Newsletter").
Body("Monthly newsletter").
Priority(mail.LowPriority).
Send()Custom Mailer Instance
For advanced use cases, create and manage your own mailer:
import (
"github.com/velocitykode/velocity/pkg/mail"
"github.com/velocitykode/velocity/pkg/mail/drivers"
)
// Create custom mailer
mailer := drivers.NewLogDriver()
mail.SetDefaultMailer(mailer)
// Or use directly
msg := mail.NewMessage().
To("user@example.com").
Subject("Test").
Body("Content")
mailer.Send(context.Background(), msg)API Reference
Global Functions
// Send an email using the default mailer
func Send(ctx context.Context, msg *Message) error
// Create a new message
func NewMessage() *Message
// Set the default mailer
func SetDefaultMailer(mailer Mailer)
// Reinitialize the mail driver
func Reinitialize() error
func ReinitializeWithDriver(driver string) errorMessage Methods
// Recipients
msg.From(email string, name ...string) *Message
msg.To(email string, name ...string) *Message
msg.CC(email string, name ...string) *Message
msg.BCC(email string, name ...string) *Message
msg.ReplyTo(email string, name ...string) *Message
// Content
msg.Subject(subject string) *Message
msg.Body(text string) *Message // Convenience for TextBody
msg.TextBody(text string) *Message
msg.HTMLBody(html string) *Message
msg.Template(name string, data interface{}) *Message
// Attachments
msg.AttachFile(path string) *Message
msg.AttachData(data []byte, name, contentType string) *Message
// Metadata
msg.Header(key, value string) *Message
msg.Priority(priority Priority) *Message
// Sending
msg.Send() error
msg.SendWithContext(ctx context.Context) errorPriority Constants
const (
LowPriority Priority = iota
NormalPriority
HighPriority
)Best Practices
Use the Log Driver in Development: Set
MAIL_DRIVER=logto avoid sending real emails during developmentAlways Set From Address: Configure
MAIL_FROM_ADDRESSin your.envto ensure all emails have a valid senderProvide Both Text and HTML: Include both
TextBody()andHTMLBody()for better email client compatibility:msg.TextBody("Plain text version"). HTMLBody("<h1>HTML version</h1>")Use Templates for Complex Emails: Don’t build HTML in code - use the
Template()methodHandle Errors Properly: Always check the error returned from
Send():if err := msg.Send(); err != nil { log.Error("Failed to send email", "error", err) return err }Use Context for Timeouts: When sending emails in HTTP handlers, use context with timeout:
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second) defer cancel() mail.Send(ctx, msg)Validate Email Addresses: Validate user-provided email addresses before attempting to send
Use Appropriate Drivers:
- Development:
logdriver - Staging:
localSMTP or test Postmark account - Production:
postmarkormailgunfor reliability
- Development:
Testing
Using the Log Driver
func TestEmailSending(t *testing.T) {
// Set log driver for testing
mail.ReinitializeWithDriver("log")
err := mail.NewMessage().
To("test@example.com").
Subject("Test").
Body("Test email").
Send()
assert.NoError(t, err)
}Mocking the Mailer
type MockMailer struct {
SentMessages []*mail.Message
}
func (m *MockMailer) Send(ctx context.Context, msg *mail.Message) error {
m.SentMessages = append(m.SentMessages, msg)
return nil
}
func TestUserRegistration(t *testing.T) {
mock := &MockMailer{}
mail.SetDefaultMailer(mock)
// Test your code that sends emails
RegisterUser("test@example.com")
// Verify email was sent
assert.Equal(t, 1, len(mock.SentMessages))
assert.Equal(t, "test@example.com", mock.SentMessages[0].GetTo()[0].Email)
}Examples
Welcome Email
func SendWelcomeEmail(user User) error {
return mail.NewMessage().
To(user.Email, user.Name).
Subject("Welcome to Our Platform").
Template("welcome", map[string]interface{}{
"Name": user.Name,
"VerifyURL": generateVerificationURL(user),
}).
Send()
}Password Reset
func SendPasswordResetEmail(email, token string) error {
resetURL := fmt.Sprintf("https://example.com/reset?token=%s", token)
return mail.NewMessage().
To(email).
Subject("Password Reset Request").
Template("password-reset", map[string]interface{}{
"ResetURL": resetURL,
"ExpiresIn": "24 hours",
}).
Send()
}Invoice with PDF
func SendInvoice(customer Customer, invoice Invoice) error {
pdfData := generateInvoicePDF(invoice)
return mail.NewMessage().
To(customer.Email, customer.Name).
Subject(fmt.Sprintf("Invoice #%s", invoice.Number)).
Template("invoice", map[string]interface{}{
"Invoice": invoice,
"Customer": customer,
}).
AttachData(pdfData, "invoice.pdf", "application/pdf").
Send()
}Notification with Priority
func SendAlertEmail(admins []string, alert Alert) error {
msg := mail.NewMessage().
Subject(fmt.Sprintf("ALERT: %s", alert.Title)).
Body(alert.Message).
Priority(mail.HighPriority)
for _, email := range admins {
msg.To(email)
}
return msg.Send()
}
