Push (Firebase FCM)
Mobile push notifications via Firebase Cloud Messaging. iOS, Android, and Web.
Mobile push is handled by Firebase Cloud Messaging (FCM). One driver, all three platforms (iOS via APNs under the hood, Android directly, and Web via browser push).
Pricing
Free. FCM has no per-message cost for any volume. You pay for whatever storage/analytics features you enable on Firebase, but message delivery itself is unlimited and free.
Setup
- Create a Firebase project at https://console.firebase.google.com.
- Add your apps (iOS, Android, Web).
- For iOS, upload your APNs authentication key (P8) in Project Settings → Cloud Messaging.
- For Android, FCM works out of the box once
google-services.jsonis in the app. - Generate a service account JSON (Project Settings → Service accounts → Generate new private key).
import (
"github.com/gopackx/go-notification/channel/push"
"github.com/gopackx/go-notification/channel/push/fcm"
)
driver, err := fcm.New(fcm.Config{
ProjectID: "my-firebase-project",
ServiceAccountJSON: []byte(os.Getenv("FCM_SERVICE_ACCOUNT_JSON")),
})
if err != nil {
panic(err)
}
notifier.RegisterChannel(driver)FCM is the only driver whose New returns (*Driver, error) — it parses
the service-account key up front. Capture and check the error; you can't inline
it into RegisterChannel.
Storing the full JSON in an env var is fine for smaller setups; for anything serious, fetch it from a secrets manager at startup.
Sending
func (n ChatMessage) Via(u notification.Notifiable) []string {
return []string{"push"}
}
func (n ChatMessage) ToPush(u notification.Notifiable) *push.Message {
return push.NewMessage().
SetTitle(n.From).
SetBody(n.Preview).
SetData("conversation_id", n.ConversationID). // string values only
SetBadge(1).
SetSound("default")
}
func (u User) RouteNotificationFor(channel string) string {
if channel == "push" {
return u.DeviceToken // the user's current FCM token
}
return ""
}RouteNotificationFor returns a single token. To target several devices in one call, set them in ToPush with SetTokens(...) (FCM multicast); use SetTopic(...) for topic delivery.
return push.NewMessage().SetTokens(t1, t2, t3).SetTitle("Shipped")Configuration reference
| Field | Type | Required | Description |
|---|---|---|---|
ProjectID | string | yes | GCP project ID that owns the FCM app. |
ServiceAccountJSON | []byte | yes | Raw bytes of the GCP service-account key file. |
Timeout | time.Duration | no | HTTP timeout per send. Default: 30s. |
Name | string | no | Override the channel name. Default: "push". |
Message fields
The push.Message builder is platform-agnostic — FCM maps these to each platform:
push.NewMessage().
SetTitle("Alert").
SetBody("Your order shipped").
SetImage("https://example.com/hero.png").
SetSound("default").
SetBadge(1).
SetClickAction("OPEN_ORDERS").
SetData("order_id", "A-1024") // string→string onlyTargeting: SetToken(one), SetTokens(many...) (multicast), or SetTopic(name) — mutually exclusive.
Token lifecycle
- Register tokens at app startup / after login and store them per user.
- Remove a token when FCM returns
UNREGISTEREDorINVALID_ARGUMENTon send — the device uninstalled the app or the token rotated. The driver surfaces these inOnError. - Refresh — FCM may rotate tokens; the client SDK emits a
onTokenRefreshevent. Send the new token to your backend and replace the old one.
Troubleshooting
UNREGISTERED— user uninstalled the app or logged out. Remove the token from your database.QUOTA_EXCEEDED— rare; usually from sending to too many tokens in one multicast. Split into smaller batches.- Silent pushes not delivered on iOS — Apple heavily throttles silent/content-available pushes. Don't rely on them for anything critical.
- Notifications work in dev, fail in TestFlight/prod — wrong APNs environment (sandbox vs production) in the service account config, or APNs P8 key uploaded to wrong project.