What you will build
Prerequisites
- , with FCM connected and device tokens syncing
- A published with push and email content
What this adds over calling FCM yourself
You already have Firebase, so calling FCM from the same function is one line shorter. It is worth being precise about what the extra hop buys, because “use a platform” is not an argument. You address a person, not a device. FCM needs a registration token. Courier needs auser_id, and resolves the tokens itself, including the case where someone has three devices and one stale token.
A failed push becomes an email. FCM can tell you a token was rejected. It cannot then reach the person another way. One routing line does.
Preferences apply without a conditional. If the recipient muted this notification type, Courier filters it. With raw FCM that check is code you write and maintain at every call site.
One log covers both channels. A push that failed and an email that succeeded are the same message in , not two systems to correlate.
If none of those matter for a given notification, call FCM directly. They usually start mattering around the second channel.
Set it up
1
Install and store the key
In your functions directory:
2
Write the trigger
This is a 2nd-gen Firestore trigger. The path carries a wildcard, and its value arrives on Two lines are doing the work worth noticing.
event.params.functions/index.js
routing: { method: "single", channels: ["push", "email"] } walks the list and stops at the first channel that works. Push first, email as its failover. Swap to all and both go out every time.if (!snapshot) return guards the case where the document is gone by the time the function runs. Firestore triggers are not transactional with the write.3
Deploy
Verify
1
Write a document
Add a comment document under a path matching the trigger, from your app or the Firebase console.
2
Check the function ran
3
Confirm which channel delivered
Open . The timeline names the channel that delivered. To prove the fallback, remove the user’s device tokens and write again. The same send should arrive by email.
Adapt it for other writes
The update case is the one to be careful with. A trigger on every write fires on fields nobody cares about, so compare before and after and return early. That is the difference between a useful alert and a reason to mute your app.
For fan-out, when many people are watching the document rather than one, address a list instead of a user. covers that shape.