Examples
Multi-Channel Notification
One notification, fanned out to email, WhatsApp, and the database in parallel.
The appeal of a unified notification library is that one Send() reaches the user everywhere. This example shows an OrderShipped going to mail + WhatsApp + database in one call.
Setup
import (
"context"
"log"
"os"
"strconv"
notification "github.com/gopackx/go-notification"
"github.com/gopackx/go-notification/channel/database"
"github.com/gopackx/go-notification/channel/mail"
"github.com/gopackx/go-notification/channel/mail/mailgun"
"github.com/gopackx/go-notification/channel/whatsapp"
"github.com/gopackx/go-notification/channel/whatsapp/waha"
"github.com/gopackx/go-notification/migrate"
"database/sql"
_ "github.com/lib/pq"
)
type User struct {
ID int64
Name string
Email string
Phone string // E.164
WantsMail bool
WantsWhatsApp bool
}
func (u User) GetID() string { return strconv.FormatInt(u.ID, 10) }
func (u User) RouteNotificationFor(channel string) string {
switch channel {
case "mail":
return u.Email
case "whatsapp":
return u.Phone
case "database":
return u.GetID()
}
return ""
}Notification
type OrderShipped struct {
OrderID string
TrackingURL string
}
func (n OrderShipped) Via(notifiable notification.Notifiable) []string {
u := notifiable.(User)
channels := []string{"database"} // always in-app
if u.WantsMail { channels = append(channels, "mail") }
if u.WantsWhatsApp { channels = append(channels, "whatsapp") }
return channels
}
func (n OrderShipped) ToMail(_ notification.Notifiable) *mail.Message {
return mail.NewMessage().
SetSubject("Your order shipped").
SetGreeting("Hey,").
Line("Your order " + n.OrderID + " is on its way.").
Action("Track package", n.TrackingURL)
}
func (n OrderShipped) ToWhatsApp(_ notification.Notifiable) *whatsapp.Message {
return whatsapp.NewMessage().
SetText("📦 Your order *" + n.OrderID + "* shipped. Track it: " + n.TrackingURL)
}
func (n OrderShipped) ToDatabase(_ notification.Notifiable) *database.Message {
return database.NewMessage().
SetType("order.shipped").
SetTitle("Order shipped").
SetBody("Your order " + n.OrderID + " is on its way.").
AddData("order_id", n.OrderID).
AddData("tracking_url", n.TrackingURL)
}Wiring the notifier
func main() {
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil { panic(err) }
notifier := notification.New(notification.Config{
OnError: func(_ context.Context, no notification.Notifiable, channel string, err error) {
log.Printf("send failed: channel=%s err=%v", channel, err)
},
})
notifier.RegisterChannel(mailgun.New(mailgun.Config{
Domain: "mg.example.com",
APIKey: os.Getenv("MAILGUN_API_KEY"),
From: mail.Address{Name: "Acme", Address: "noreply@example.com"},
}))
notifier.RegisterChannel(waha.New(waha.Config{
BaseURL: "http://localhost:3000",
APIKey: os.Getenv("WAHA_API_KEY"),
SessionID: "default",
}))
// Create the notifications table, then register the database channel.
if err := migrate.Up(context.Background(), db, database.DialectPostgreSQL, "notifications"); err != nil {
panic(err)
}
store := database.NewSQLStore(db, database.DialectPostgreSQL)
notifier.RegisterChannel(database.New(database.Config{Store: store}))
user := User{
ID: 1, Name: "Dani", Email: "dani@example.com",
Phone: "+628123456789",
WantsMail: true, WantsWhatsApp: true,
}
ctx := context.Background()
if err := notifier.Send(ctx, user, OrderShipped{
OrderID: "A-1024",
TrackingURL: "https://example.com/track/A-1024",
}); err != nil {
log.Fatal(err)
}
notifier.Close()
}What happens on send
Via()returns["database", "mail", "whatsapp"]based onuser.WantsMailanduser.WantsWhatsApp.- Worker pool enqueues three sends, one per channel.
- Each worker calls
RouteNotificationFor(channel)to get the address, then the correspondingTo<Channel>()method to render. - All three APIs are hit in parallel.
- On any failure, retry kicks in per that channel. Others continue unaffected.
Total wall time: max(mail_latency, whatsapp_latency, db_insert_latency), not the sum.