Docs Menu

Docs HomeRealm

Manage Flexible Sync Subscriptions - React Native SDK

On this page

  • Prerequisites
  • Align Subscriptions with Backend App
  • Manage Your Subscriptions
  • Access Subscriptions
  • Add a Subscription
  • Configure a Realm with Initial Subscriptions
  • Check the Status of Subscriptions
  • Subscription State "Complete"
  • Remove Subscriptions
  • Remove a Subscription by Query
  • Remove a Subscription by Name
  • Remove a Subscription by Reference
  • Remove All Subscriptions on an Object Type
  • Remove All Subscriptions
  • Flexible Sync RQL Limitations
  • Unsupported Query Operators in Flexible Sync
  • List Queries
  • Embedded or Linked Objects

Flexible Sync uses subscriptions and permissions to determine which data to sync with your App Services App using Atlas Device Sync. The @realm/react library streamlines permissions and queries.

Note

Flexible Sync Prerequisites

Enabling Flexible Sync in your App requires a non-sharded Atlas cluster running MongoDB 5.0 or greater

Before you can add Flexible Sync subscriptions to a React Native client, you must:

  1. Configure Flexible Sync on the Atlas App Services backend

  2. Initialize the app client

  3. Authenticate a user in the client

  4. Configure a synced Realm in the client

Your client-side subscription queries must align with the Device Sync configuration in your backend App Services App.

Subscription queries return all objects of a type. You can filter results with a Realm Query Language query that includes one or more queryable fields.

To learn more about configuring queryable fields, refer to Queryable Fields in the App Services documentation.

To learn more about the limitations of using Realm Query Language with Flexible Sync, refer to the Flexible Sync RQL Limitations section.

In your client application, use the Realm.subscriptions property to manage a set of subscriptions to specific queries on queryable fields.

If you're using @realm/react, you can manage realm subscriptions inside of a properly-configured RealmProvider. The useRealm() gives you access to the currently-opened realm.

You can do the following with your subscriptions:

When the data matches the subscription, and the authenticated user has the appropriate permissions, Device Sync syncs the backend data with the client app.

Subscription sets persist across sessions even if you no longer include the subscription in your code. Subscription information is stored in the synced realm's database file. You must explicitly remove a subscription for it to stop attempting to sync matching data.

Within a <RealmProvider> that's configured for Flexible Sync, you can access a collection of all subscriptions for your app. This is called a SubscriptionSet.

import React, {useEffect} from 'react';
// get realm context from createRealmContext()
import {RealmContext} from '../RealmConfig';
import {Text, FlatList} from 'react-native';
const {useRealm, useQuery} = RealmContext;
function SubscriptionManager() {
const realm = useRealm();
// Returns a subscription set that contains all subscriptions.
const allSubscriptions = realm.subscriptions;
// Pass object model to useQuery and filter results.
// This does not create a subscription.
const seenBirds = useQuery('Bird').filtered('haveSeen == true');
useEffect(() => {
realm.subscriptions.update(mutableSubs => {
// Create subscription for filtered results.
mutableSubs.add(seenBirds);
});
});
return (
<FlatList
data={allSubscriptions}
keyExtractor={subscription => subscription.id.toString()}
renderItem={({item}) => <Text>{item.name}</Text>}
/>
);
}

You can use this SubscriptionSet to add queries to the list of subscriptions and update existing subscriptions.

Compose queries using Realm Query Language and add them to your Flexible Sync subscriptions.

To add a query to a subscription:

  1. Create a transaction by passing a callback function to the SubscriptionSet.update() method of your SubscriptionSet. The callback function provides a MutableSubscriptionSet as an argument.

  2. Within the callback function, call the MutableSubscriptionSet.add() method to add a query to the subscription.

MutableSubscriptionSet.add() takes a query and a SubscriptionOptions object. In SubscriptionOptions, you should name your subscription by adding a string value to SubscriptionOptions.name. This makes updating the subscription easier.

If you do not give your subscription a name, the name is set to null and the subscription is based on the Realm Query Language (RQL) string. If the RQL string changes and the subscription is not named, you won't be able to update your subscription.

When you create a subscription, Realm looks for data matching a query on a specific object type. In your Flexible Sync subscriptions, you can have subscriptions on several different object types or several queries on the same object type.

Add a subscription with Realm.objects(), which you can access with the useRealm() hook.

If you're using @realm/react, you can use the useQuery() hook instead of Realm.objects(). However, useQuery() needs an active subscription to return results. If no subscription has been added yet, useQuery() returns an empty result, which is not a valid query for MutableSubscriptionSet.add().

import React, {useEffect} from 'react';
// get realm context from createRealmContext()
import {RealmContext} from '../RealmConfig';
import {Text, FlatList} from 'react-native';
const {useRealm} = RealmContext;
function SubscriptionManager() {
const realm = useRealm();
const seenBirds = realm.objects('Bird').filtered('haveSeen == true');
useEffect(() => {
realm.subscriptions.update((mutableSubs, realm) => {
// Create subscription query
const seenBirdsSubQuery = realm
.objects('Bird')
.filtered('haveSeen == true');
// Create subscription for filtered results.
mutableSubs.add(seenBirdsSubQuery, {name: 'seenBirds'});
});
});
return (
<FlatList
data={seenBirds}
keyExtractor={bird => bird._id.toString()}
renderItem={({item}) => <Text>{item._id}</Text>}
/>
);
}

You must have at least one subscription before you can read from or write to a Flexible Sync realm. Initial subscriptions let you define subscriptions when you configure a synced realm.

To open a synced realm with initial subscriptions, add an initialSubscriptions property to <RealmProvider>'s SyncConfiguration object.

You cannot use the @realm/react library hooks useQuery and useObject when setting initial subscriptions. Instead, use the Node.js read and write operations.

import React from 'react';
import {AppProvider, UserProvider} from '@realm/react';
// get realm context from createRealmContext()
import {RealmContext} from '../RealmConfig';
import {Text, FlatList} from 'react-native';
const {RealmProvider, useQuery} = RealmContext;
function AppWrapper() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
sync={{
flexible: true,
initialSubscriptions: {
update(subs, realm) {
subs.add(realm.objects('Turtle'));
},
},
onError: console.log,
}}>
<SubscriptionManager />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}
function SubscriptionManager() {
// Pass object model to useQuery to get all objects of type `Turtle`.
// These results automatically update with changes from other devices
// because we created a subscription with `initialSubscriptions`.
const allTurtles = useQuery('Turtle');
return (
<FlatList
data={allTurtles}
keyExtractor={turtle => turtle._id.toString()}
renderItem={({item}) => <Text>{item._id}</Text>}
/>
);
}

You can check the state of a SubscriptionSet to see if the server has acknowledged all subscriptions and the device has downloaded the data locally.

You can use subscription state to:

  • Trigger error handling

  • Show if the transaction is pending or has completed

  • Find out when a subscription set is superseded, and you should obtain a new instance of the subscription set to write a subscription change

import React, {useEffect} from 'react';
import {Text, View} from 'react-native';
// get realm context from createRealmContext()
import {RealmContext} from '../RealmConfig';
const {useRealm, useQuery} = RealmContext;
function SubscriptionManager() {
const realm = useRealm();
useEffect(() => {
realm.subscriptions.update((mutableSubs, realm) => {
// Create subscription for filtered results.
mutableSubs.add(realm.objects('Bird').filtered('haveSeen == true'));
});
});
// Returns state of all subscriptions, not individual subscriptions.
// In this case, it's just the subscription for `Bird` objects where
// `haveSeen` is true.
const allSubscriptionState = realm.subscriptions.state;
return (
<View>
<Text>Status of all subscriptions: {allSubscriptionState}</Text>
</View>
);
}

The subscription set state "complete" does not mean "sync is done" or "all documents have been synced". "Complete" means the following two things have happened:

  • The subscription has become the active subscription set that is currently being synchronized with the server.

  • The documents that matched the subscription at the time the subscription was sent to the server are now on the local device. Note that this does not necessarily include all documents that currently match the subscription.

The Realm SDK does not provide a way to check whether all documents that match a subscription have synced to the device.

You can remove subscriptions in several ways:

  • Remove a single subscription with the given query

  • Remove a single subscription with the given name

  • Remove all subscriptions on a specific type

  • Remove all subscriptions

When you remove a subscription query, the server also removes synced data from the client device.

Examples on this page assume you're working with @realm/react and a properly-configured RealmProvider.

import {useEffect} from 'react';
// get realm context from createRealmContext()
import {RealmContext} from '../RealmConfig';
const {useRealm} = RealmContext;
function SubscriptionManager() {
const realm = useRealm();
useEffect(() => {
realm.subscriptions.update(mutableSubs => {
// Remove subscription for object type `Turtle`,
// which we added in `initialSubscriptions`.
mutableSubs.removeByObjectType('Turtle');
});
});
return (
// ...
);
}

You can remove a specific subscription by query by executing a transaction on the subscriptions set. Pass the query to MutableSubscriptionSet.remove() within a write transaction.

realm.subscriptions.update((mutableSubs) => {
// remove a subscription with a specific query
mutableSubs.remove(tasks.filtered('owner == "Ben"'));
});

To remove a specific subscription by name, execute a transaction on the subscriptions set. Within the transaction, pass the name to MutableSubscriptionSet.removeByName().

realm.subscriptions.update((mutableSubs) => {
// remove a subscription with a specific name
mutableSubs.removeByName("longRunningTasksSubscription");
});

If you have a reference to a subscription, you can remove that subscription. To do so, execute a transaction on the subscriptions set. Within the transaction, pass the reference variable to MutableSubscriptionSet.removeSubscription().

let subscriptionReference;
realm.subscriptions.update((mutableSubs) => {
subscriptionReference = mutableSubs.add(realm.objects("Task"));
});
// later..
realm.subscriptions.removeSubscription(subscriptionReference);

To remove all subscriptions on a specific object type, execute a transaction on the subscriptions set. Within the transaction, pass the object type as a string to MutableSubscriptionSet.removeByObjectType().

realm.subscriptions.update((mutableSubs) => {
mutableSubs.removeByObjectType("Team");
});

To remove all subscriptions from the subscriptions set, execute a transaction on the subscriptions set. Call MutableSubscriptionSet.removeAll() within a write transaction.

realm.subscriptions.update((mutableSubs) => {
mutableSubs.removeAll();
});

Flexible Sync has some limitations when using RQL operators. When you write the query subscription that determines which data to sync, the server does not support these query operators. However, you can still use the full range of RQL features to query the synced data set in the client application.

Operator Type
Unsupported Operators
Aggregate Operators
@avg, @count, @max, @min, @sum
Query Suffixes
DISTINCT, SORT, LIMIT

Case insensitive queries ([c]) cannot use indexes effectively. As a result, case insensitive queries are not recommended, since they could lead to performance problems.

Flexible Sync only supports @count for array fields.

Flexible Sync supports querying lists using the IN operator.

You can query a list of constants to see if it contains the value of a queryable field:

// Query a constant list for a queryable field value
"priority IN { 1, 2, 3 }"

If a queryable field has an array value, you can query to see if it contains a constant value:

// Query an array-valued queryable field for a constant value
"'comedy' IN genres"

Warning

You cannot compare two lists with each other in a Flexible Sync query. Note that this is valid Realm Query Language syntax outside of Flexible Sync queries.

// Invalid Flexible Sync query. Do not do this!
"{'comedy', 'horror', 'suspense'} IN genres"
// Another invalid Flexible Sync query. Do not do this!
"ANY {'comedy', 'horror', 'suspense'} != ANY genres"

Flexible Sync does not support querying on properties in Embedded Objects or links. For example, obj1.field == "foo".

←  Configure a Synced Realm - React Native SDKManage a Sync Session - React Native SDK →
Share Feedback
© 2023 MongoDB, Inc.

About

  • Careers
  • Investor Relations
  • Legal Notices
  • Privacy Notices
  • Security Information
  • Trust Center
© 2023 MongoDB, Inc.