Sync Changes Between Devices - Node.js SDK
On this page
Prerequisites
Before you can access a synced realm from the client, you must:
Enable sync in the App Services UI.
Authenticate a user in your client project.
Open a Flexible Synced Realm or Open a Partition-Based Synced Realm
Note
Sync method in the examples on this page
The examples on this page use Partition-Based Sync with the firehose partition strategy. This means that data is synced to the clients of all users with the same static partition value.
In the examples on this page, the static partition value is MyPartitionValue
.
All realms with the partition MyPartitionValue
have access to any document with a matching partition value.
The realm's Configuration object includes
a SyncConfiguration with
partitionValue: "MyPartitionValue"
.
const config = { schema: [DogSchema], // predefined schema sync: { user: app.currentUser, partitionValue: "MyPartitionValue", ...rest of SyncConfiguration }, };
For more information on how to configure Partition-Based Sync, refer to the Partition Strategies documentation.
Sync Changes in the Background
You may want to sync changes in the background to display partial data to the user while the synced realm downloads data from the server, preventing the user experience from being blocked. We recommend syncing changes in the background for applications in which the user's device may go offline. To sync changes in the background, open a synced realm synchronously.
Create a OpenRealmBehaviorConfiguration object and set its
type
to "openImmediately"
.
const OpenRealmBehaviorConfiguration = { type: "openImmediately", };
Create a Configuration object, which must
include the sync
property defining a SyncConfiguration object. Set this
OpenRealmBehaviorConfiguration
object as the value for
the newRealmFileBehavior
and existingRealmFileBehavior
fields of the
SyncConfiguration
.
const config = { schema: [DogSchema], // predefined schema sync: { user: app.currentUser, partitionValue: "MyPartitionValue", // The behavior to use when this is the first time opening a realm. newRealmFileBehavior: OpenRealmBehaviorConfiguration, // The behavior to use when a realm file already exists locally, // i.e. you have previously opened the realm. existingRealmFileBehavior: OpenRealmBehaviorConfiguration, }, };
Finally, call Realm.open() to open a synced realm. This will create a sync session and begin downloading any existing data from the server in the background.
const realm = await Realm.open(config);
Pause or Resume a Sync Session
Opening a synced realm starts a sync session. You can pause and resume the sync session on the realm. Pausing a sync session only pauses that realm's sync session. If you have more than one open realm, pause does not affect the sync sessions for other realms.
To pause synchronization, use the syncSession.pause() method. To resume synchronization, use the syncSession.resume() method.
const OpenRealmBehaviorConfiguration = { type: "openImmediately", }; var config = { schema: [DogSchema], // predefined schema sync: { user: app.currentUser, partitionValue: "MyPartitionValue", newRealmFileBehavior: OpenRealmBehaviorConfiguration, existingRealmFileBehavior: OpenRealmBehaviorConfiguration, }, }; let realm = await Realm.open(config); const syncSession = realm.syncSession; // Pause synchronization syncSession.pause(); // Later, resume synchronization syncSession.resume();
Note
Use the .pause()
method to control when a device syncs.
You should only use it for temporary and short-term pauses of syncing.
Examples of when to use .pause()
include:
Syncing data only at specified time of day
Conserving device battery use
Don't use the .pause()
method to stop syncing for
indefinite time periods or time ranges in months and years. The functionality
is not designed or tested for these use cases, and you could encounter a range of issues
when using it this way.
Check Upload & Download Progress for a Sync Session
To check the upload and download progress for a sync session, add a progress notification using the syncSession.addProgressNotification() method.
The syncSession.addProgressNotification()
method takes in the following three parameters:
A
direction
parameter. Set to"upload"
to register notifications for uploading data. Set to"download"
to register notifications for downloading data.A
mode
parameter. Set to"reportIndefinitely"
for the notifications to continue until the callback is unregistered using syncSession.removeProgressNotification(). Set to"forCurrentlyOutstandingWork"
for the notifications to continue until only the currently transferable bytes are synced.A callback function parameter that has the arguments
transferred
andtransferable
.transferred
is the current number of bytes already transferred.transferable
is the total number of bytes already transferred plus the number of bytes pending transfer.
Note
Flexible Sync progress notifications are not yet fully supported. When using Flexible Sync, downloads only report notifications after changes are integrated. Partition-Based Sync provides ongoing notifications as changes progress downloading. Uploads report ongoing progress notifications for both Sync Modes.
Example
In the following example, an application developer registers a callback on the syncSession
to
listen for upload events indefinitely. The developer writes to the realm and
then unregisters the syncSession
notification callback.
const OpenRealmBehaviorConfiguration = { type: "openImmediately", }; var config = { schema: [DogSchema], // predefined schema sync: { user: app.currentUser, partitionValue: "MyPartitionValue", newRealmFileBehavior: OpenRealmBehaviorConfiguration, existingRealmFileBehavior: OpenRealmBehaviorConfiguration, }, }; let realm = await Realm.open(config); const syncSession = realm.syncSession; syncSession.addProgressNotification( "upload", "reportIndefinitely", (transferred, transferable) => { console.log(`${transferred} bytes has been transferred`); console.log( `There are ${transferable} total transferable bytes, including the ones that have already been transferred` ); } ); // Upload something let dog; realm.write(() => { dog = realm.create("Dog", { name: "Fido", age: 2, }); }); // use dog // remember to unregister the progress notifications syncSession.removeProgressNotification((transferred, transferable) => { console.log(`There was ${transferable} total transferable bytes`); console.log(`${transferred} bytes were transferred`); });
Check the Network Connection
To check the current state of the connection to the server, call the syncSession.connectionState() method.
Tip
Realm's offline-first design means that you generally don't need to
check the current network connection state. That said, the
syncSession.connectionState()
method is available if your app needs to
gets the current state of the connection to the server.
var config = { schema: [DogSchema], // predefined schema sync: { user: app.currentUser, partitionValue: "MyPartitionValue", }, }; let realm = await Realm.open(config); const syncSession = realm.syncSession; const connectionState = syncSession.connectionState();
Set the Client Log Level
You can set the realm Sync client log level by calling Realm.App.Sync.setLogLevel() with your Realm.App.
Example
In the following example, an application developer sets the sync client
log level to "debug"
.
Realm.App.Sync.setLogLevel(app, "debug");
Tip
To diagnose and troubleshoot errors while developing your application, set the
log level to debug
or trace
. For production deployments, decrease the
log level for improved performance.
Multiplex Sync Sessions
Enable session multiplexing to consolidate multiple sync sessions of a Realm app. Only use session multiplexing if you see errors about reaching the file descriptor limit, and you know you are using many sync sessions.
To enable session multiplexing, call Realm.App.Sync.enableSessionMultiplexing() with your Realm.App.
Example
Realm.App.Sync.enableSessionMultiplexing(app);
Handle Sync Errors
While developing an application that uses Device Sync, you should set an error handler. This error handler will detect and respond to any failed sync-related API calls.
Set an error handler by registering an error callback as part of the SyncConfiguration.
var config = { schema: [DogSchema], // predefined schema sync: { user: app.currentUser, partitionValue: "MyPartitionValue", error: (_session, error) => { (error) => { console.log(error.name, error.message); }; }, }, }; const realm = await Realm.open(config);
Tip
For a list of common Device Sync errors and how to handle them, refer to Sync Errors in the App Services Device Sync documentation.
Perform a Client Reset
You can customize behavior in the event of a client reset error with a custom error handler function:
let realm = await Realm.open(config); function errorSync(_session, error) { if (realm) { if (error.name === "ClientReset") { const realmPath = "<Your Realm Path>"; realm.close(); console.log(`Error ${error.message}, need to reset ${realmPath}…`); Realm.App.Sync.initiateClientReset(app, realmPath); // pass your realm app instance, and realm path to initiateClientReset() console.log(`Creating backup from ${error.config.path}…`); // Move backup file to a known location for a restore fs.renameSync(error.config.path, realmPath + "~"); // Discard the reference to the realm instance realm = null; } else { console.log(`Received error ${error.message}`); } } } var config = { schema: [DogSchema], // predefined schema sync: { user: app.currentUser, partitionValue: "MyPartitionValue", error: errorSync, }, };
Tip
To see how to recover unsynced local changes in a client reset, check out this example on GitHub.