Email System
The Base framework provides a flexible email system that supports multiple email providers through a unified interface. This allows you to easily send emails from your application and switch between providers as needed.
Features
- Support for multiple email providers (SMTP, SendGrid, Postmark)
- Simple API for sending emails
- HTML and plain text email support
- Easy configuration through environment variables
- Fallback to default sender for development
Configuration
Email configuration is handled through environment variables:
# Email configuration
MAIL_DRIVER=smtp # Options: smtp, sendgrid, postmark, default
MAIL_FROM=no-reply@example.com
# SMTP Configuration
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=username
MAIL_PASSWORD=password
# SendGrid Configuration
SENDGRID_API_KEY=your_sendgrid_api_key
# Postmark Configuration
POSTMARK_API_KEY=your_postmark_api_key
Basic Usage
Initializing the Email System
The email system is typically initialized during application startup:
import "base/core/email"
// Initialize email system with your application's configuration
if err := email.Initialize(cfg); err != nil {
log.Fatalf("Failed to initialize email system: %v", err)
}
Sending a Simple Email
// Send a plain text email
msg := email.Message{
To: []string{"user@example.com"},
Subject: "Welcome to Base",
Body: "Thank you for joining us!",
IsHTML: false,
}
if err := email.Send(msg); err != nil {
log.Error("Failed to send email:", err)
}
Sending an HTML Email
// Send an HTML email
msg := email.Message{
To: []string{"user@example.com"},
Subject: "Welcome to Base",
Body: "<h1>Welcome!</h1><p>Thank you for joining us!</p>",
IsHTML: true,
}
if err := email.Send(msg); err != nil {
log.Error("Failed to send email:", err)
}
Supported Providers
SMTP
The SMTP provider allows you to send emails through any SMTP server:
// SMTP configuration
type SMTPConfig struct {
Host string
Port int
Username string
Password string
From string
}
// Automatically created from environment variables
sender, err = NewSMTPSender(cfg)
SendGrid
The SendGrid provider allows you to send emails through the SendGrid API:
// SendGrid configuration
type SendGridConfig struct {
APIKey string
From string
}
// Automatically created from environment variables
sender, err = NewSendGridSender(cfg)
Postmark
The Postmark provider allows you to send emails through the Postmark API:
// Postmark configuration
type PostmarkConfig struct {
APIKey string
From string
}
// Automatically created from environment variables
sender, err = NewPostmarkSender(cfg)
Default Sender
For development and testing, Base includes a default sender that logs emails instead of actually sending them:
// For development environments
sender, err = NewDefaultSender(cfg)
Advanced Usage
Custom Email Templates
You can use Go's template system to create dynamic emails:
import "text/template"
// Create a template
tmpl, err := template.New("welcome").Parse(`
<h1>Welcome, {{.Name}}!</h1>
<p>Thank you for joining us. Your account has been created successfully.</p>
<p>Click <a href="{{.ActivationLink}}">here</a> to activate your account.</p>
`)
if err != nil {
log.Error("Failed to parse template:", err)
return
}
// Render the template
data := struct {
Name string
ActivationLink string
}{
Name: user.Name,
ActivationLink: fmt.Sprintf("https://example.com/activate/%s", user.ActivationToken),
}
var body bytes.Buffer
if err := tmpl.Execute(&body, data); err != nil {
log.Error("Failed to render template:", err)
return
}
// Send the email
msg := email.Message{
To: []string{user.Email},
Subject: "Welcome to Base",
Body: body.String(),
IsHTML: true,
}
if err := email.Send(msg); err != nil {
log.Error("Failed to send email:", err)
}
Integrating with Events
You can combine the email system with the event system to send emails in response to events:
// Listen for user registration events
emitter.On("user.registered", func(data interface{}) {
if user, ok := data.(*models.User); ok {
// Send welcome email
msg := email.Message{
To: []string{user.Email},
Subject: "Welcome to Base",
Body: fmt.Sprintf("Welcome, %s! Thank you for registering.", user.Name),
IsHTML: false,
}
if err := email.Send(msg); err != nil {
log.Error("Failed to send welcome email:", err)
}
}
})
Implementation Details
The email system is implemented using a simple provider pattern:
// Message represents an email message
type Message struct {
To []string
From string
Subject string
Body string
IsHTML bool
}
// Sender interface for all email providers
type Sender interface {
Send(msg Message) error
}
// Factory function to create the appropriate sender
func NewSender(cfg *config.Config) (Sender, error) {
switch cfg.EmailProvider {
case "smtp":
return NewSMTPSender(cfg)
case "sendgrid":
return NewSendGridSender(cfg)
case "postmark":
return NewPostmarkSender(cfg)
case "default":
return NewDefaultSender(cfg)
case "":
logrus.Warnf("EMAIL_PROVIDER not set, using default sender")
return NewDefaultSender(cfg)
default:
return nil, fmt.Errorf("unsupported email provider: %s", cfg.EmailProvider)
}
}
Best Practices
Use Environment Variables: Store email credentials in environment variables, never hardcode them.
Implement Retries: For production applications, consider implementing retry logic for failed email attempts.
Async Email Sending: For better performance, consider sending emails asynchronously using goroutines.
Email Templates: Use templates for all emails to maintain consistency and make updates easier.
Testing: Use the default sender during testing to avoid sending actual emails.
Testing Emails
For testing, you can use the default sender which logs emails instead of sending them:
// Configure for testing
os.Setenv("MAIL_DRIVER", "default")
// Initialize the email system
email.Initialize(cfg)
// Send test email - will be logged instead of sent
msg := email.Message{
To: []string{"test@example.com"},
Subject: "Test Email",
Body: "This is a test email.",
IsHTML: false,
}
email.Send(msg)
Alternatively, you can use services like Mailtrap for testing SMTP emails in development environments.