---
title: Server Integration
description: >-
  Integrate FCM with a backend server: register()/onRegistered() device
  registration, saving Installation IDs to a database, sending to devices or
  topics with Admin SDKs, and signing out users with unregister(), plus the
  deprecated getToken()/onTokenRefresh()/deleteToken().
next: /messaging/migrating-to-installation-id
previous: /messaging/ios-notification-images
---
> ## Documentation Index
> Fetch the complete documentation index at: https://rnfirebase.io/llms.txt
> Use this file to discover all available pages before exploring further.

## Prerequisites

- `@react-native-firebase/app` and `@react-native-firebase/messaging` installed (`npm install --save @react-native-firebase/app @react-native-firebase/messaging` or `yarn add @react-native-firebase/app @react-native-firebase/messaging`), then rebuild native apps (`cd ios && pod install` on iOS). From v26, New Architecture is required for packages with a native bridge.
- Native Firebase config completed per [Getting Started](/) (Android `google-services.json`, iOS `GoogleService-Info.plist`, Expo config plugin, or React Native CLI setup).
- On iOS, complete [iOS Messaging Setup](/messaging/usage/ios-setup) and register for remote messages / request notification permission before calling `register` in production flows.
- Optional for the datastore samples below: `@react-native-firebase/auth` and `@react-native-firebase/firestore` installed the same way, with Auth sign-in and a Firestore `users` collection available.
- A server environment with a Firebase Admin SDK (examples use Node.js [`firebase-admin`](https://www.npmjs.com/package/firebase-admin)) or the FCM HTTP v1 API.

The Cloud Messaging module provides the tools required to enable you to send custom messages directly from your own servers.
For example, you could send a FCM message to a specific device when a new chat message is saved to your database
and display a [notification](/messaging/notifications) or update local device storage so the message is instantly available.

Firebase provides a number of SDKs in different languages such as [Node.JS](https://www.npmjs.com/package/firebase-admin),
[Java](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/messaging/package-summary),
[Python](https://firebase.google.com/docs/reference/admin/python/firebase_admin.messaging),
[C#](https://firebase.google.com/docs/reference/admin/dotnet/namespace/firebase-admin/messaging) and
[Go](https://godoc.org/firebase.google.com/go/messaging). It also supports sending messages over
[HTTP](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages). These methods allow you to send messages
directly to your user's devices via the FCM servers.

## Device registration (latest)

This is the latest way to register a device with FCM: `register` and `onRegistered` (Installation ID messaging). Installation ID messaging is off by default, see [Installation ID messaging](#installation-id-messaging) to opt in. The deprecated token APIs are documented after it, see [Saving tokens (deprecated)](#saving-tokens-deprecated).

To send a message to a device, you must access its unique identifier. With Installation ID messaging, the device registers with `register` and the Cloud Messaging
module delivers its Firebase Installation ID (FID) to `onRegistered`. The FID should be saved inside of your systems data-store and
should be easily accessible when required.

The examples below use a [Cloud Firestore](/firestore) database to store and manage the Installation IDs, and [Authentication](/auth)
to manage the users identity. You can however use any datastore or authentication method of your choice.

> If using iOS, ensure you have completed [iOS Messaging Setup](/messaging/usage/ios-setup) and requested user permission (see [iOS Permissions](/messaging/ios-permissions)) before trying to receive messages.

## Saving the Installation ID

Once your application has started, you can subscribe with `onRegistered` and call `register` on the Cloud Messaging module to get the unique
device FID:

```jsx
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import { getMessaging, onRegistered, register } from '@react-native-firebase/messaging';
import { getAuth } from '@react-native-firebase/auth';
import { getFirestore, doc, updateDoc, arrayUnion } from '@react-native-firebase/firestore';

async function saveTokenToDatabase(token) {
  const currentUser = getAuth().currentUser;
  if (!currentUser) {
    console.warn('No signed-in user; skip saving device ID');
    return;
  }

  await updateDoc(doc(getFirestore(), 'users', currentUser.uid), {
    tokens: arrayUnion(token),
  });
}

function App() {
  useEffect(() => {
    const messaging = getMessaging();

    const unsubscribe = onRegistered(messaging, installationId => {
      saveTokenToDatabase(installationId);
    });

    register(messaging).catch(error => {
      console.warn('FCM registration failed', error);
    });

    return unsubscribe;
  }, []);

  return (
    <View>
      <Text>FCM registration sync started</Text>
    </View>
  );
}

export default App;
```

The above code snippet has a single purpose; storing the device FID on a remote database. The `useEffect` is fired
when the `App` component runs and registers the device. `onRegistered` also fires when the FID rotates.

Inside of the `saveTokenToDatabase` method, we store the FID on a record specifically relating to the current user. You may also
notice that the FID is being added via the `arrayUnion` method. A user can have more than one device (for example using 2 devices)
so it's important to ensure that we store every device's Installation ID in the database.

## Installation ID messaging

For a concise migration guide (API mapping, checklist, error codes), see
[Migrating to Installation ID](/messaging/migrating-to-installation-id).

Firebase Installation ID (FID) registration identifies the app instance to FCM.
React Native Firebase exposes `register`, `unregister`, `onRegistered`, and `onUnregistered` for that path.

**The FID flag is off by default.** Opt in via `firebase.json` (preferred for bare React Native), or set the
native keys / Expo plugin prop directly:

### Bare React Native (`firebase.json`)

```json
// <projectRoot>/firebase.json
{
  "react-native": {
    "messaging_installation_id_enabled": true
  }
}
```

This maps to Android meta-data `firebase_messaging_installation_id_enabled` and iOS Info.plist
`FirebaseMessagingInstallationIdEnabled` (same pattern as `messaging_auto_init_enabled`).

### Bare React Native (manual native keys)

If you prefer not to use `firebase.json`, set the keys yourself:

**Android**: in your app `AndroidManifest.xml` (library Manifest already emits this key
defaulting to `false`; use `tools:replace` so your value wins):

```xml
<!-- add xmlns:tools="http://schemas.android.com/tools" on the <manifest> root if missing -->
<meta-data
  android:name="firebase_messaging_installation_id_enabled"
  android:value="true"
  tools:replace="android:value" />
```

**iOS**: in `Info.plist`:

```xml
<key>FirebaseMessagingInstallationIdEnabled</key>
<true/>
```

### Expo

```json
{
  "expo": {
    "plugins": [
      [
        "@react-native-firebase/messaging",
        {
          "installationIdEnabled": true
        }
      ]
    ]
  }
}
```

`installationIdEnabled: true` writes both native keys. Omitted or `false` writes nothing.

When the flag is off, `register` and `unregister` reject, and `onRegistered` and `onUnregistered` throw, with
`messaging/installation-id-not-enabled`.

`register()` does **not** call `registerDeviceForRemoteMessages`. On iOS you still need APNs registration
before remote pushes arrive (auto-registration by default, or a manual `registerDeviceForRemoteMessages` call).

```jsx
import {
  getMessaging,
  register,
  unregister,
  onRegistered,
  onUnregistered,
  registerDeviceForRemoteMessages,
} from '@react-native-firebase/messaging';

async function registerAndUnregister() {
  const messaging = getMessaging();

  // iOS: only needed when auto-registration is disabled (no-op on Android).
  await registerDeviceForRemoteMessages(messaging);

  const unsubscribeRegistered = onRegistered(messaging, installationId => {
    // Upload the FID to your backend
    console.log('registered', installationId);
  });

  await register(messaging);

  const unsubscribeUnregistered = onUnregistered(messaging, installationId => {
    console.log('unregistered', installationId);
  });

  await unregister(messaging);

  // Call both unsubscribe functions when the listeners are no longer needed, for example in an effect cleanup.
  return () => {
    unsubscribeRegistered();
    unsubscribeUnregistered();
  };
}
```

`onRegistered` replays the cached FID to a new subscriber while the app is still registered, so subscribing after
`register()` has completed still delivers the current FID. Later registrations and FID rotations fire as usual.
If your callback throws during that initial replay, `onRegistered` throws the same error and no subscription is created.
`onUnregistered` does not replay: it only fires for an `unregister()` that completes after you subscribe.

## Saving tokens (deprecated)

The Messaging token APIs `getToken`, `deleteToken` and `onTokenRefresh` are deprecated. Firebase JS 12.14+, Android Messaging 25.1+ and Apple 12.18+ deprecated the FCM token APIs in favor of Installation ID registration (`register` / `onRegistered`, and `unregister` / `onUnregistered` in place of `deleteToken`). The token APIs still work while Installation ID messaging is off. Once you opt in, `getToken` and `deleteToken` reject and `onTokenRefresh` throws with `messaging/token-api-disabled`.

Once your application has started, you can call the `getToken` method on the Cloud Messaging module to get the unique
device token (if using a different push notification provider, such as Amazon SNS, you will need to call `getAPNSToken` on iOS):

```jsx
// DEPRECATED: prefer register() and onRegistered() above. Still works while Installation ID messaging is off.
import React, { useEffect } from 'react';
import { View, Text, Platform } from 'react-native';
import {
  getMessaging,
  getToken,
  getAPNSToken,
  onTokenRefresh,
} from '@react-native-firebase/messaging';
import { getAuth } from '@react-native-firebase/auth';
import { getFirestore, doc, updateDoc, arrayUnion } from '@react-native-firebase/firestore';

async function saveTokenToDatabase(token) {
  const currentUser = getAuth().currentUser;
  if (!currentUser) {
    console.warn('No signed-in user; skip saving FCM token');
    return;
  }

  await updateDoc(doc(getFirestore(), 'users', currentUser.uid), {
    tokens: arrayUnion(token),
  });
}

function App() {
  useEffect(() => {
    const messaging = getMessaging();

    getToken(messaging).then(token => {
      return saveTokenToDatabase(token);
    });

    if (Platform.OS === 'ios') {
      getAPNSToken(messaging).then(token => {
        if (token) {
          return saveTokenToDatabase(token);
        }
        return undefined;
      });
    }

    return onTokenRefresh(messaging, token => {
      saveTokenToDatabase(token);
    });
  }, []);

  return (
    <View>
      <Text>FCM token sync registered</Text>
    </View>
  );
}

export default App;
```

The above code snippet has a single purpose; storing the device FCM token on a remote database. The `useEffect` is fired
when the `App` component runs and immediately gets the token. It also listens to any events when the device automatically refreshes
the token.

Inside of the `saveTokenToDatabase` method, we store the token on a record specifically relating to the current user. You may also
notice that the token is being added via the `arrayUnion` method. A user can have more than one token (for example using 2 devices)
so it's important to ensure that we store all tokens in the database.

## Sending to stored IDs

With the Installation IDs (or FCM tokens, if you still use the deprecated token APIs) stored in a secure datastore, you can send messages via FCM to those devices.

> The following example uses the Node.JS `firebase-admin` package to send messages to our devices, however any SDK (listed above)
> can be used.

Go ahead and setup the [`firebase-admin`](https://www.npmjs.com/package/firebase-admin) library on your server environment.
Once setup, our script needs to perform two actions:

1. Fetch the stored IDs required to send the message (the `tokens` array saved above, whether it holds Installation IDs or FCM tokens).
2. Send a data payload to the devices those IDs are registered to.

Imagine our application being similar to Instagram. Users are able to upload pictures, and other users can "like" those pictures.
Each time a post is liked, we want to send a message to the user that uploaded the picture. The code below simulates a function
which is called with all of the information required when a picture is liked:

```js
// codeblock-ignore: server-side sample that requires the firebase-admin package
const admin = require('firebase-admin');

// ownerId - who owns the picture someone liked
// userId - id of the user who liked the picture
// picture - metadata about the picture

async function onUserPictureLiked(ownerId, userId, picture) {
  const ownerSnap = await admin.firestore().collection('users').doc(ownerId).get();
  const userSnap = await admin.firestore().collection('users').doc(userId).get();
  const owner = ownerSnap.data();
  const user = userSnap.data();

  await admin.messaging().sendEachForMulticast({
    tokens: owner.tokens,
    data: {
      owner: JSON.stringify(owner),
      user: JSON.stringify(user),
      picture: JSON.stringify(picture),
    },
    android: {
      // Required for background/quit data-only messages on Android
      priority: 'high',
    },
    apns: {
      payload: {
        aps: {
          // Required for background/quit data-only messages on iOS
          // Note: iOS frequently will receive the message but decline to deliver it to your app.
          // This is an Apple design choice to favor user battery life over data-only delivery
          // reliability. It is not under app control, though you may see the behavior in device logs.
          'content-available': true,
        },
      },
    },
  });
}

await onUserPictureLiked('owner-id', 'user-id', { name: 'Sunset' });
```

Data-only messages are sent as low priority on both Android and iOS and will not trigger the `setBackgroundMessageHandler`
by default. To enable this functionality, you must set the "priority" to `high` on Android and enable the
`content-available` flag for iOS in the message payload. When you send data messages to Apple devices, Firebase requires
the `apns-priority` header to be `5`; a high priority data message is rejected with `INVALID_ARGUMENT`.

> If using the FCM REST API, see the [following documentation](https://firebase.google.com/docs/cloud-messaging/http-server-ref) on setting `priority` and `content-available`!

The `data` property can send an object of key-value pairs totaling `4KB` as string values (hence the `JSON.stringify`). Do not use `from`, `message_type`, or any key starting with `google.`, `gcm.` or `gcm.notification.` as a data key, because those are reserved.

Back within our application, as explained in the [Usage](/messaging/usage) documentation, our message handlers will receive a
[`RemoteMessage`](https://reference.rnfirebase.io/_react-native-firebase/messaging/types/messaging/RemoteMessage.html) payload containing the message details sent from the server:

```jsx
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import { getMessaging, onMessage } from '@react-native-firebase/messaging';

function App() {
  useEffect(() => {
    const messaging = getMessaging();
    const unsubscribe = onMessage(messaging, async remoteMessage => {
      const user = JSON.parse(String(remoteMessage.data?.user));
      const picture = JSON.parse(String(remoteMessage.data?.picture));

      console.log(`The user "${user.name}" liked your picture "${picture.name}"`);
    });

    return unsubscribe;
  }, []);

  return (
    <View>
      <Text>Foreground message listener registered</Text>
    </View>
  );
}

export default App;
```

Your application code can then handle messages as you see fit; updating local cache, displaying a [notification](/messaging/notifications)
or updating UI.

## Signing out users

The FID is associated with the instance of the installed app. By default, the FID only changes when the app is reinstalled, its data is cleared, or Firebase rotates it (`onRegistered` fires again when that happens).

This means that by default, if your app has users and you allow them to log out and log in on the same app on the same device, the same FID will be used for both users. Usually this is not what you want, so you must take care to cycle the FID at the same time you handle user logout/login.

How and when you invalidate an FID and generate a new one will be specific to your project, but a common pattern is to call `unregister` during logout and update your back end to remove the FID, then to call `register` during login and update your back end systems to associate the new FID with the logged in user.

```js
import { getMessaging, onRegistered, register, unregister } from '@react-native-firebase/messaging';

async function cycleFidOnLogout() {
  const messaging = getMessaging();
  await unregister(messaging);
}

async function cycleFidOnLogin() {
  const messaging = getMessaging();
  const unsubscribe = onRegistered(messaging, installationId => {
    console.log('Associate this FID with the signed-in user:', installationId);
  });
  await register(messaging);
  return unsubscribe;
}

await cycleFidOnLogout();
await cycleFidOnLogin();
```

Note that when you call `unregister`, the current FID is no longer active.

### Signing out with the deprecated token APIs

These APIs are deprecated, see the deprecation note under [Saving tokens (deprecated)](#saving-tokens-deprecated).

Firebase Cloud Messaging tokens are associated with the instance of the installed app. By default, only token expiration or uninstalling/reinstalling the app will generate a fresh token. A common pattern is to delete the FCM token during logout and update your back end to remove it, then to fetch the FCM token during login and update your back end systems to associate the new token with the logged in user.

```js
// DEPRECATED: prefer unregister() and register() above. Still works while Installation ID messaging is off.
import { getMessaging, deleteToken, getToken } from '@react-native-firebase/messaging';

async function cycleFcmTokenOnLogout() {
  const messaging = getMessaging();
  await deleteToken(messaging);
}

async function cycleFcmTokenOnLogin() {
  const messaging = getMessaging();
  const token = await getToken(messaging);
  console.log('Associate this token with the signed-in user:', token);
  return token;
}

await cycleFcmTokenOnLogout();
await cycleFcmTokenOnLogin();
```

Note that when a token is deleted by calling the `deleteToken` method, it is immediately and permanently invalid.

## Send messages to topics

When devices [subscribe to topics](/messaging/usage#topics), you can send messages without specifying/storing any device
tokens.

Using the `firebase-admin` Admin SDK as an example, we can send a message to devices subscribed to a topic:

```js
// codeblock-ignore: server-side sample that requires the firebase-admin package
const admin = require('firebase-admin');

const message = {
  data: {
    type: 'warning',
    content: 'A new weather warning has been created!',
  },
  topic: 'weather',
};

admin
  .messaging()
  .send(message)
  .then(response => {
    console.log('Successfully sent message:', response);
  })
  .catch(error => {
    console.log('Error sending message:', error);
  });
```

## Conditional topics

To send a message to a combination of topics, specify a condition, which is a boolean expression that specifies the target
topics. For example, the following condition will send messages to devices that are subscribed to `weather` and either `news`
or `traffic`:

```json
condition: "'weather' in topics && ('news' in topics || 'traffic' in topics)"
```

To send a message to this condition, replace the `topic` key with `condition`:

```js
// codeblock-ignore: server-side sample that requires the firebase-admin package
const admin = require('firebase-admin');

const message = {
  data: {
    content: 'New updates are available!',
  },
  condition: "'weather' in topics && ('news' in topics || 'traffic' in topics)",
};

admin
  .messaging()
  .send(message)
  .then(response => {
    console.log('Successfully sent message:', response);
  })
  .catch(error => {
    console.log('Error sending message:', error);
  });
```

## Send messages with image

Both the Notifications composer and the FCM API support image links in the message payload.

### iOS

To successfully send an image using the Admin SDK it's important that the `ApnsConfig` options are set:

```js
const iosImagePayload = {
  notification: {
    body: 'This is an FCM notification that displays an image!',
    title: 'FCM Notification',
  },
  apns: {
    payload: {
      aps: {
        'mutable-content': 1,
      },
    },
    fcmOptions: {
      imageUrl: 'image-url',
    },
  },
};

console.log(iosImagePayload);
```

> Check out the [official Firebase documentation](https://firebase.google.com/docs/cloud-messaging/ios/send-image) to see the list of available configuration for iOS.

### Android

Similarly to iOS, some configurations specific to Android are needed:

```js
const androidImagePayload = {
  notification: {
    body: 'This is an FCM notification that displays an image!',
    title: 'FCM Notification',
  },
  android: {
    notification: {
      imageUrl: 'image-url',
    },
  },
};

console.log(androidImagePayload);
```

> If you want to know more about sending an image on Android have a look at [the documentation](https://firebase.google.com/docs/cloud-messaging/android/send-image).

## Pulling it all together

It's possible to send one notification that will be delivered to both platforms using the Admin SDK:

```js
// codeblock-ignore: server-side sample that requires the firebase-admin package
const admin = require('firebase-admin');

const registrationTokens = ['YOUR_REGISTRATION_TOKEN_1', 'YOUR_REGISTRATION_TOKEN_2'];

const message = {
  tokens: registrationTokens,
  notification: {
    body: 'This is an FCM notification that displays an image!',
    title: 'FCM Notification',
  },
  apns: {
    payload: {
      aps: {
        'mutable-content': 1,
      },
    },
    fcmOptions: {
      imageUrl: 'image-url',
    },
  },
  android: {
    notification: {
      imageUrl: 'image-url',
    },
  },
};

admin
  .messaging()
  .sendEachForMulticast(message)
  .then(response => {
    console.log('Successfully sent message:', response);
  })
  .catch(error => {
    console.log('Error sending message:', error);
  });
```

If you want to read more about building send requests with the Admin SDK check out [this link](https://firebase.google.com/docs/cloud-messaging/send/v1-api).
