コンソールへ移動

ウェブや JavaScript でメッセージをトピックに送信する

パブリッシュ / サブスクライブ モデルに基づく FCM トピック メッセージングでは、特定のトピックにオプトインした複数のデバイスにメッセージを送信できます。必要に応じて作成したトピック メッセージを FCM がルーティング処理し、確実に正しいデバイスにメッセージを配信します。

たとえば、地域の潮汐予測アプリのユーザーは、「潮流警報」トピックにオプトインし、特定の地域の海釣りに最適な状況の通知を受信できます。スポーツアプリのユーザーは、お気に入りのチームの実況ゲームスコアの自動更新に登録できます。

トピックに関する留意点を以下に示します。

  • トピック メッセージングは、天気、その他の広く入手可能な情報コンテンツに最適です。
  • トピック メッセージは、レイテンシではなくスループットに対して最適化されます。単一のデバイスまたはデバイスの小グループに高速で安全に配信する場合は、トピックではなく登録トークンをメッセージのターゲットとしてください
  • 各ユーザーの複数のデバイスにメッセージを送信する必要がある場合は、そのようなユースケース向けのデバイス グループ メッセージングを検討してください。
  • トピック メッセージングでは、トピックごとの登録数に制限はありませんが、FCM により次の制限が課されます。
    • 1 つのアプリ インスタンスを登録できるのは、2,000 トピックまでです。
    • バッチ インポートを使用してアプリ インスタンスを登録する場合、各リクエストのアプリ インスタンスの数が 1,000 に制限されます。
    • 新規登録の頻度がプロジェクトごとに制限されます。短期間に多数の登録リクエストを送信すると、FCM サーバーから 429 RESOURCE_EXHAUSTED(「割り当てを超過した」)というレスポンスが返されます。この場合は、指数バックオフを使用して再試行します。

クライアント アプリをトピックに登録する

登録トークンとトピック名がわかっていれば、Google Instance ID サーバー API を使用して、トピックにトークンを追加できます。このエンドポイントで Instance ID API を呼び出して、アプリ インスタンスの登録トークンとトピック名を指定します。

 https://iid.googleapis.com/iid/v1/<REGISTRATION_TOKEN>/rel/topics/<TOPIC_NAME>

たとえば、「movies」という名前のトピックにアプリ インスタンスを登録するには、次に示すように、認証ヘッダーにサーバー API キーを追加して POST リクエストをサーバーからエンドポイントに送信します。

https://iid.googleapis.com/iid/v1/nKctODamlM4:CKrh_PC8kIb7O...clJONHoA/rel/topics/movies
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA

サーバーキーの機密性を維持するため、このタイプのリクエストをクライアントから送信しないでください。

リクエストが成功した場合は、HTTP 200 OK が返されます。エラー レスポンスの詳細とバッチ リクエストの送信方法については、アプリ インスタンス用の関係マップの作成をご覧ください。

登録トークンのリストを Firebase Admin SDK 登録メソッドに渡すことにより、対応するデバイスをトピックに登録できます。

Node.js

// These registration tokens come from the client FCM SDKs.
var registrationTokens = [
  'YOUR_REGISTRATION_TOKEN_1',
  // ...
  'YOUR_REGISTRATION_TOKEN_n'
];

// Subscribe the devices corresponding to the registration tokens to the
// topic.
admin.messaging().subscribeToTopic(registrationTokens, topic)
  .then(function(response) {
    // See the MessagingTopicManagementResponse reference documentation
    // for the contents of response.
    console.log('Successfully subscribed to topic:', response);
  })
  .catch(function(error) {
    console.log('Error subscribing to topic:', error);
  });

Java

// These registration tokens come from the client FCM SDKs.
List<String> registrationTokens = Arrays.asList(
    "YOUR_REGISTRATION_TOKEN_1",
    // ...
    "YOUR_REGISTRATION_TOKEN_n"
);

// Subscribe the devices corresponding to the registration tokens to the
// topic.
TopicManagementResponse response = FirebaseMessaging.getInstance().subscribeToTopic(
    registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
System.out.println(response.getSuccessCount() + " tokens were subscribed successfully");

Python

# These registration tokens come from the client FCM SDKs.
registration_tokens = [
    'YOUR_REGISTRATION_TOKEN_1',
    # ...
    'YOUR_REGISTRATION_TOKEN_n',
]

# Subscribe the devices corresponding to the registration tokens to the
# topic.
response = messaging.subscribe_to_topic(registration_tokens, topic)
# See the TopicManagementResponse reference documentation
# for the contents of response.
print(response.success_count, 'tokens were subscribed successfully')

Go

// These registration tokens come from the client FCM SDKs.
registrationTokens := []string{
	"YOUR_REGISTRATION_TOKEN_1",
	// ...
	"YOUR_REGISTRATION_TOKEN_n",
}

// Subscribe the devices corresponding to the registration tokens to the
// topic.
response, err := client.SubscribeToTopic(ctx, registrationTokens, topic)
if err != nil {
	log.Fatalln(err)
}
// See the TopicManagementResponse reference documentation
// for the contents of response.
fmt.Println(response.SuccessCount, "tokens were subscribed successfully")

C#

// These registration tokens come from the client FCM SDKs.
var registrationTokens = new List<string>()
{
    "YOUR_REGISTRATION_TOKEN_1",
    // ...
    "YOUR_REGISTRATION_TOKEN_n",
};

// Subscribe the devices corresponding to the registration tokens to the
// topic
var response = await FirebaseMessaging.DefaultInstance.SubscribeToTopicAsync(
    registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
Console.WriteLine($"{response.SuccessCount} tokens were subscribed successfully");

Admin FCM API を使用すると、登録トークンを適切なメソッドに渡すことにより、トピックからデバイスを登録解除することもできます。

Node.js

// These registration tokens come from the client FCM SDKs.
var registrationTokens = [
  'YOUR_REGISTRATION_TOKEN_1',
  // ...
  'YOUR_REGISTRATION_TOKEN_n'
];

// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
admin.messaging().unsubscribeFromTopic(registrationTokens, topic)
  .then(function(response) {
    // See the MessagingTopicManagementResponse reference documentation
    // for the contents of response.
    console.log('Successfully unsubscribed from topic:', response);
  })
  .catch(function(error) {
    console.log('Error unsubscribing from topic:', error);
  });

Java

// These registration tokens come from the client FCM SDKs.
List<String> registrationTokens = Arrays.asList(
    "YOUR_REGISTRATION_TOKEN_1",
    // ...
    "YOUR_REGISTRATION_TOKEN_n"
);

// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
TopicManagementResponse response = FirebaseMessaging.getInstance().unsubscribeFromTopic(
    registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
System.out.println(response.getSuccessCount() + " tokens were unsubscribed successfully");

Python

# These registration tokens come from the client FCM SDKs.
registration_tokens = [
    'YOUR_REGISTRATION_TOKEN_1',
    # ...
    'YOUR_REGISTRATION_TOKEN_n',
]

# Unubscribe the devices corresponding to the registration tokens from the
# topic.
response = messaging.unsubscribe_from_topic(registration_tokens, topic)
# See the TopicManagementResponse reference documentation
# for the contents of response.
print(response.success_count, 'tokens were unsubscribed successfully')

Go

// These registration tokens come from the client FCM SDKs.
registrationTokens := []string{
	"YOUR_REGISTRATION_TOKEN_1",
	// ...
	"YOUR_REGISTRATION_TOKEN_n",
}

// Unsubscribe the devices corresponding to the registration tokens from
// the topic.
response, err := client.UnsubscribeFromTopic(ctx, registrationTokens, topic)
if err != nil {
	log.Fatalln(err)
}
// See the TopicManagementResponse reference documentation
// for the contents of response.
fmt.Println(response.SuccessCount, "tokens were unsubscribed successfully")

C#

// These registration tokens come from the client FCM SDKs.
var registrationTokens = new List<string>()
{
    "YOUR_REGISTRATION_TOKEN_1",
    // ...
    "YOUR_REGISTRATION_TOKEN_n",
};

// Unsubscribe the devices corresponding to the registration tokens from the
// topic
var response = await FirebaseMessaging.DefaultInstance.UnsubscribeFromTopicAsync(
    registrationTokens, topic);
// See the TopicManagementResponse reference documentation
// for the contents of response.
Console.WriteLine($"{response.SuccessCount} tokens were unsubscribed successfully");

subscribeToTopic() メソッドと unsubscribeFromTopic() メソッドにより、FCM からのレスポンスを含むオブジェクトが生成されます。戻り値の型は、リクエストで指定された登録トークンの数に関係なく同じ形式です。

エラー(認証の失敗、無効なトークンまたはトピックなど)の場合、これらのメソッドの結果はエラーになります。すべてのエラーコード、その説明、解決手順を記載した一覧については、Admin FCM API のエラーをご覧ください。

トピック メッセージを受信して処理する

FCM は、他のダウンストリーム メッセージと同じようにトピック メッセージを配信します。クライアントでメッセージを処理する方法は、ウェブページの状態(フォアグラウンドかバックグラウンドか)や、このセクションで説明する要因によって異なります。

メッセージの動作は、ページがフォアグラウンドにある(フォーカスされている)か、バックグラウンドにあって他のタブに隠されているか、完全に閉じられているかによって異なります。いずれの場合も、onMessage コールバックをページで処理する必要がありますが、バックグラウンドの場合は、これに加えて setBackgroundMessageHandler を処理するか、ユーザーがウェブアプリをフォアグラウンドに戻せるように表示通知を構成する必要があります。

アプリの状態 通知 データ ペイロード 両方
フォアグラウンド onMessage onMessage onMessage
バックグラウンド(Service Worker) SDK によって表示される通知 setBackgroundMessageHandler SDK によって表示される通知

ウェブアプリがフォアグラウンドの場合にメッセージを処理する

アプリが onMessage イベントを受信するには、firebase-messaging-sw.js で Firebase メッセージング Service Worker を定義する必要があります。あるいは useServiceWorker で、既存の Service Worker を指定できます。

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/6.3.4/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/6.3.4/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
  'messagingSenderId': 'YOUR-SENDER-ID'
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

アプリがフォアグラウンドにある(ユーザーがウェブページを現在閲覧している)場合は、そのページでデータと通知ペイロードを直接受信できます。

// Handle incoming messages. Called when:
// - a message is received while the app has focus
// - the user clicks on an app notification created by a service worker
//   `messaging.setBackgroundMessageHandler` handler.
messaging.onMessage((payload) => {
  console.log('Message received. ', payload);
  // ...
});

ウェブアプリがバックグラウンドの場合にメッセージを処理する

アプリがバックグラウンドで動作中になんらかのメッセージを受信すると、ブラウザ内の表示通知がトリガーされます。この通知に関するオプション(タイトルやクリック アクションなど)は、アプリサーバーからの送信リクエスト、またはクライアント上の Service Worker ロジックで指定できます。

送信リクエストでの通知オプションの設定

アプリサーバーから送信される通知メッセージの場合は、FCM JavaScript API が fcm_options.link キーをサポートします。通常、これはウェブアプリ内のページに設定されます。

https://fcm.googleapis.com//v1/projects/<YOUR-PROJECT-ID>/messages:send
Content-Type: application/json
Authorization: bearer <YOUR-ACCESS-TOKEN>

{
  "message": {
    "topic": "matchday"
    "notification": {
      "title": "Background Message Title",
      "body": "Background message body"
    },
    "webpush": {
      "fcm_options": {
        "link": "https://dummypage.com"
      }
    }
  }
}

リンク値が指しているページがすでにブラウザタブで開いている場合は、通知をクリックするとそのタブがフォアグラウンドに移動します。そのページがまだ開いていない場合は、通知をクリックするとそのページが新しいタブで開きます。

データ メッセージは fcm_options.link をサポートしないため、すべてのデータ メッセージに通知ペイロードを追加することをおすすめします。または、Service Worker を使用して通知を処理することもできます。

通知メッセージとデータ メッセージの違いの説明については、メッセージのタイプをご覧ください。

Service Worker での通知オプションの設定

データ メッセージの場合、Service Worker で通知オプションを設定できます。まず、Service Worker でアプリを初期化します。

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/6.3.4/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/6.3.4/firebase-messaging.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
  'messagingSenderId': 'YOUR-SENDER-ID'
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();

オプションを設定するには、firebase-messaging-sw.jssetBackgroundMessageHandler を呼び出します。この例では、title、body、icon の各フィールドを含む通知を作成します。

messaging.setBackgroundMessageHandler(function(payload) {
  console.log('[firebase-messaging-sw.js] Received background message ', payload);
  // Customize notification here
  const notificationTitle = 'Background Message Title';
  const notificationOptions = {
    body: 'Background Message body.',
    icon: '/firebase-logo.png'
  };

  return self.registration.showNotification(notificationTitle,
    notificationOptions);
});

送信リクエストを作成する

トピックを作成した後、クライアント側でクライアント アプリ インスタンスをトピックに登録するか、またはサーバー API を使用することによって、トピックにメッセージを送信できます。バックエンドの送信ロジック内で、次のように目的のトピック名を指定します。

Node.js

// The topic name can be optionally prefixed with "/topics/".
var topic = 'highScores';

var message = {
  data: {
    score: '850',
    time: '2:45'
  },
  topic: topic
};

// Send a message to devices subscribed to the provided topic.
admin.messaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

Java

// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";

// See documentation on defining a message payload.
Message message = Message.builder()
    .putData("score", "850")
    .putData("time", "2:45")
    .setTopic(topic)
    .build();

// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# The topic name can be optionally prefixed with "/topics/".
topic = 'highScores'

# See documentation on defining a message payload.
message = messaging.Message(
    data={
        'score': '850',
        'time': '2:45',
    },
    topic=topic,
)

# Send a message to the devices subscribed to the provided topic.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

Go

// The topic name can be optionally prefixed with "/topics/".
topic := "highScores"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Topic: topic,
}

// Send a message to the devices subscribed to the provided topic.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

C#

// The topic name can be optionally prefixed with "/topics/".
var topic = "highScores";

// See documentation on defining a message payload.
var message = new Message()
{
    Data = new Dictionary<string, string>()
    {
        { "score", "850" },
        { "time", "2:45" },
    },
    Topic = topic,
};

// Send a message to the devices subscribed to the provided topic.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);

REST

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
  "message":{
    "topic" : "foo-bar",
    "notification" : {
      "body" : "This is a Firebase Cloud Messaging Topic Message!",
      "title" : "FCM Message"
      }
   }
}

cURL コマンド:

curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
  "message": {
    "topic" : "foo-bar",
    "notification": {
      "body": "This is a Firebase Cloud Messaging Topic Message!",
      "title": "FCM Message"
    }
  }
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

トピックの組み合わせにメッセージを送信するには、条件を指定します。条件は、ターゲット トピックを指定するブール式です。たとえば次の条件では、TopicA に加えて TopicBTopicC のどちらか一方にも登録されているデバイスにメッセージが送信されます。

"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"

条件式はまずかっこ内から評価され、次に左から右に評価されます。上記の式では、いずれか 1 つのトピックだけに登録したユーザーにはメッセージは送られません。同様に、TopicA に登録していないユーザーにもメッセージは送られません。メッセージが送られるのは、次の組み合わせに登録している場合のみです。

  • TopicATopicB
  • TopicATopicC

条件式には最大 5 つのトピックを含めることができます。

条件に送信するには:

Node.js

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
var message = {
  notification: {
    title: '$GOOG up 1.43% on the day',
    body: '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.'
  },
  condition: condition
};

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
admin.messaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

Java

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
String condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
Message message = Message.builder()
    .setNotification(new Notification(
        "$GOOG up 1.43% on the day",
        "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day."))
    .setCondition(condition)
    .build();

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# Define a condition which will send to devices which are subscribed
# to either the Google stock or the tech industry topics.
condition = "'stock-GOOG' in topics || 'industry-tech' in topics"

# See documentation on defining a message payload.
message = messaging.Message(
    notification=messaging.Notification(
        title='$GOOG up 1.43% on the day',
        body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
    ),
    condition=condition,
)

# Send a message to devices subscribed to the combination of topics
# specified by the provided condition.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

Go

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
condition := "'stock-GOOG' in topics || 'industry-tech' in topics"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Condition: condition,
}

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

C#

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
var message = new Message()
{
    Notification = new Notification()
    {
        Title = "$GOOG up 1.43% on the day",
        Body = "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
    },
    Condition = condition,
};

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);

REST

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
   "message":{
    "condition": "'dogs' in topics || 'cats' in topics",
    "notification" : {
      "body" : "This is a Firebase Cloud Messaging Topic Message!",
      "title" : "FCM Message",
    }
  }
}

cURL コマンド:

curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
  "notification": {
    "title": "FCM Message",
    "body": "This is a Firebase Cloud Messaging Topic Message!",
  },
  "condition": "'dogs' in topics || 'cats' in topics"
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

次のステップ