💨

【FCM】Cloud functionsでfirestoreにメッセージ追加時プッシュ通知

2022/05/17に公開

はじめに

こんにちは

今回はFirestoreにデータ追加時、Functionsを使ってプッシュ通知をする機能の実装方法を簡潔に紹介します

準備

  • firebase-cli
  • プロジェクト

Firestoreの構造

今回は例としてこのようなデータ構造だとします。
イメージとしてはメッセージの変更を検知したら、対象Roomのユーザートークンを取得し、プッシュ通知を行う感じです

rooms:
  messages:
    createdAt: timestamp
    body: String
  users:
    fcmToken: String

実装方法

まずプロジェクト直下でfirebase initをします

firebase init functions

実行したら色々聞かれると思うので、ご自分の環境に合わせて選択してください。
Typescriptを選択すると初期状態からエラーが発生していたので、今回はJSを選択しております。

完了したらプロジェクト直下にfunctionsディレクトリーが出来上がっているので、その中のindex.jsを修正していきます。

まず、必要なモジュールなどを定義します

const functions = require("firebase-functions");
const admin = require("firebase-admin")
const db = admin.firestore()

admin.initializeApp()

メッセージの変更を検知したいので、

exports.messagePushNotify = functions.firestore.document("rooms/{roomId}/messages/{messageId}")
    .onCreate(async (snap, context) => {

このように書きます。

次にプッシュ通知を行う内容を定義します。
メッセージの内容はfirestoreのmessagesから新しく追加されたメッセージの内容をとってきます。
snap.data().bodyの形で今回の場合は取得できますので、

const payload = {
    notification: {
        title: "アプリ名など",
        body: snap.data().body,
    }
}

このような形になります。

次に配信先のFCM tokenを取得します。
まずはUsersまでのPathを定義し、データを持ってきます

const roomId = context.params.roomId
const usersRef = db.collection('rooms').doc(roomId).collection('users')
const snapshot = await usersRef.get()
if (snapshot.empty) {
    console.log('No matching user')
    return
}

今回はドキュメントパスにワイルドカードパターンを使用しているので、このような形で対象RoomIDが取得できます。

// functions.firestore.document("rooms/{roomId}/messages/{messageId}")
const roomId = context.params.roomId

最後にトークンを取得し、対象ユーザーへプッシュ通知の処理を入れれば終了です

snapshot.forEach(doc => {
    // push notify
    const token = doc.data()['fcmToken']
    admin.messaging().sendToDevice(token, payload)
})

全体のソースコード

const functions = require("firebase-functions");
const admin = require("firebase-admin")
const db = admin.firestore()

admin.initializeApp()

// Cloud Firestore trigger
exports.messagePushNotify = functions.firestore.document("rooms/{roomId}/messages/{messageId}")
    .onCreate(async (snap, context) => {
        // Get roomId from params
        const roomId = context.params.roomId

        const title = "アプリ名"
        const message = snap.data().body

        // Notification Details
        const payload = {
            notification: {
                title: title,
                body: message,
            }
        }

        // Get the list of device notification tokens.
        const usersRef = db.collection('rooms').doc(roomId).collection('users')
        const snapshot = await usersRef.get()
        if (snapshot.empty) {
            console.log('No matching user')
            return
        }

        snapshot.forEach(doc => {
            // push notify
            const token = doc.data()['fcmToken']
            admin.messaging().sendToDevice(token, payload)
        })
    })

参考

https://firebase.google.com/docs/functions/firestore-events?hl=ja
https://github.com/firebase/functions-samples/blob/main/fcm-notifications/functions/index.js

Discussion