go-notificationgo-notification
API Reference

Notifiable Interface

The contract your recipient types implement — how to be identified and reached per channel.

A Notifiable is anything that can receive a notification — typically your User type, but nothing stops you from implementing it on Team, Organization, or any recipient concept in your domain.

Interface

main.go
type Notifiable interface {
    GetID() string
    RouteNotificationFor(channel string) string
}

Two methods, both returning string:

  • GetID() — a stable identifier for this recipient. It's stored alongside persisted notifications (the database channel) so the same recipient can be looked up later.
  • RouteNotificationFor(channel) — the address/route for the given channel (an email, a phone number, a device token, a URL). Return an empty string to skip this channel for this recipient.

Typical implementation

main.go
type User struct {
    ID             string
    Email          string
    Phone          string // E.164
    TelegramChatID string
    DeviceToken    string
}

func (u User) GetID() string { return u.ID }

func (u User) RouteNotificationFor(channel string) string {
    switch channel {
    case "mail":     return u.Email
    case "sms":      return u.Phone
    case "whatsapp": return u.Phone
    case "chat":     return u.TelegramChatID // chat is the default name for chat drivers
    case "push":     return u.DeviceToken
    }
    return ""
}

Route value per channel

The route string is interpreted by the receiving driver:

Channel nameRoute stringExample
mailemail address"user@example.com"
smsphone (E.164)"+628123456789"
whatsappphone (E.164)"+628123456789"
chatchat ID / channel / webhook URL (driver-dependent)"123456789", "#ops"
pusha single device token"fcm-token…"
webhooktarget URL"https://hooks…"
databasenot routed — uses GetID()

The channel name passed to RouteNotificationFor is the driver's Name(), not the provider. By default chat drivers report "chat", mail drivers "mail", etc. If you register a driver under a custom name (via its ChannelName/Name config field), route on that same string.

Returning an empty string

If the recipient can't receive on a channel, return "". The channel is skipped for that recipient and dispatch moves on.

main.go
func (u User) RouteNotificationFor(channel string) string {
    if channel == "sms" && u.Phone == "" {
        return "" // user hasn't added a phone number
    }
    // ...
}

Multiple destinations

Routing returns a single string. To send to multiple device tokens in one push, set them in the notification's ToPush method instead of relying on the route:

main.go
func (n OrderShipped) ToPush(notification.Notifiable) *push.Message {
    return push.NewMessage().
        SetTokens(token1, token2, token3). // FCM multicast
        SetTitle("Shipped")
}

To send the same notification to multiple recipients, model each as its own Notifiable and use SendMulti:

main.go
admins := []notification.Notifiable{admin1, admin2, admin3}
notifier.SendMulti(ctx, admins, AlertFired{})

Non-user notifiables

Team, Channel, Organization — anything can be a Notifiable:

main.go
type SlackChannel struct {
    ID      string
    Webhook string
}

func (c SlackChannel) GetID() string { return c.ID }

func (c SlackChannel) RouteNotificationFor(channel string) string {
    if channel == "chat" {
        return c.Webhook
    }
    return ""
}

notifier.Send(ctx, SlackChannel{ID: "ops", Webhook: "https://hooks.slack.com/…"}, IncidentFired{})

NotifiableType (optional)

For the database channel, each stored row records a notifiable_type and notifiable_id (GetID()). The type defaults to "notifiables". Override it by implementing the optional NotifiableType interface:

main.go
func (u User) GetNotifiableType() string { return "users" }

notification.NotifiableTypeOf(n) returns this value, falling back to "notifiables" when the method isn't implemented.

Testing

A fake notifiable is trivial:

main.go
type fakeUser struct{ id, email string }

func (f fakeUser) GetID() string { return f.id }
func (f fakeUser) RouteNotificationFor(channel string) string {
    if channel == "mail" {
        return f.email
    }
    return ""
}

Use that in unit tests to isolate notification rendering from your real user persistence.