0% found this document useful (0 votes)
16 views356 pages

Realm

Realm, now known as Atlas Device SDK, is a cross-platform, reactive, object-oriented mobile database designed for low-power environments. It features a unique database engine, supports live queries and objects, and offers network synchronization through Device Sync. The document provides implementation details, comparisons with other databases, and instructions for using the Realm React Native SDK.

Uploaded by

Chandan Santhosh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views356 pages

Realm

Realm, now known as Atlas Device SDK, is a cross-platform, reactive, object-oriented mobile database designed for low-power environments. It features a unique database engine, supports live queries and objects, and offers network synchronization through Device Sync. The document provides implementation details, comparisons with other databases, and instructions for using the Realm React Native SDK.

Uploaded by

Chandan Santhosh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 356

Realm - React Native SDK

Realm is now Atlas Device SDK – Learn More

Realm is a reactive, object-oriented, cross-platform, mobile database:

• Reactive: query the current state of data and subscribe to state changes
like the result of a query, or even changes to a single object.

• Object-oriented: organizes data as objects, rather than rows,


documents, or columns.

• Cross-platform: use the same database on iOS, Android, Linux, macOS,


or Windows. Just define a schema for each SDK you use.

• Mobile: designed for the low-power, battery-sensitive, real-time


environment of a mobile device.

Realm is a cross-platform and mobile-optimized alternative to other mobile


databases such as SQLite, Core Data, and Room.

This page explains some of the implementation details and inner workings of
Realm and Device Sync. This page is for you if you are:

• a developer interested in learning more about Realm

• comparing Realm with competing databases

• trying to understand how Realm works with Device Sync

This explanation begins with a deep dive into database internals, continues
with a high-level introduction to some of the features of Realm, and wraps up
with some of the differences of working with Device Sync and the local version
of Realm.

Database Internals
Realm uses a completely unique database engine, file format, and design. This
section describes some of the high-level details of those choices. This section
applies to both the device-local version of Realm as well as the networked
Device Sync version. Differences between the local database and the
synchronized database are explained in the Atlas Device Sync section.

Native Database Engine

Realm is an entire database written from scratch in C++, instead of building on


top of an underlying database engine like SQLite. Realm's underlying storage
layer uses B+ trees to organize objects. As a result, Realm controls
optimizations from the storage level all the way up to the access level.

Realm stores data in realms: collections of heterogeneous realm objects. You


can think of each realm as a database. Each object in a realm is equivalent to a
row in a SQL database table or a MongoDB document. Unlike SQL, realms do
not separate different object types into individual tables.

Realm stores objects as groups of property values. We call this column-based


storage. This means that queries or writes for individual objects can be slower
than row-based storage equivalents when unindexed, but querying a single
field across multiple objects or fetching multiple objects can be much faster
due to spatial locality and in-CPU vector operations.

Realm uses a zero-copy design to make queries faster than an ORM, and often
faster than raw SQLite.

Realm Files

Realm persists data in files saved on device storage. The database uses
several kinds of file:

• realm files, suffixed with "realm", e.g. default.realm: contain object


data.
• lock files, suffixed with "lock", e.g. default.realm.lock: keep track of
which versions of data in a realm are actively in use. This prevents
realm from reclaiming storage space that is still used by a client
application.

• note files, suffixed with "note", e.g. default.realm.note: enable inter-


thread and inter-process notifications.

• management files, suffixed with "management",


e.g. default.realm.management: internal state management.

Realm files contain object data with the following data structures: Groups,
Tables, Cluster Trees, and Clusters. Realm organizes these data structures
into a tree structure with the following form:

• The top level, known as a Group, stores object metadata, a transaction


log, and a collection of Tables.

• Each class in the realm schema corresponds to a Table within the top-
level Group.

• Each Table contains a Cluster Tree, an implementation of a B+ tree.

• Leaves on the Cluster Tree are called Clusters. Each contains a range of
objects sorted by key value.

• Clusters store objects as collections of columns.

• Each column contains data for a single property for multiple instances
of a given object. Columns are arrays of data with uniformly sized
values.

• Columns store data in one of the following sizes: 1, 2, 4, 8, 16, 32, or 64


bits. Each column uses one value size, determined by the largest value.

Since pointers refer to memory addresses, objects written to persistent files


cannot store references as pointers. Instead, realm files refer to data using the
offset from the beginning of the file. We call this a ref. As Realm uses memory
mapping to read and write data, database operations translate these refs from
offsets to memory pointers when navigating database structures.

Copy-on-Write: The Secret Sauce of Data Versioning

Realm uses a technique called copy-on-write, which copies data to a new


location on disk for every write operation instead of overwriting older data on
disk. Once the new copy of data is fully written, the database updates existing
references to that data. Older data is only garbage collected when it is no
longer referenced or actively in use by a client application.

Because of copy-on-write, older copies of data remain valid, since all of the
references in those copies still point to other valid data. Realm leverages this
fact to offer multiple versions of data simultaneously to different threads in
client applications. Most applications tie data refreshes to the repaint cycle of
the looper thread that controls the UI, since data only needs to refresh as often
as the UI does. Longer-running procedures on background threads, such as
large write operations, can work with a single version of data for a longer
period of time before committing their changes.

Memory Mapping

Writes use memory mapping to avoid copying data back and forth from
memory to storage. Accessors and mutators read and write to disk via
memory mapping. As a result, object data is never stored on the stack or heap
of your app. By default, data is memory-mapped as read-only to prevent
accidental writes.

Realm uses operating system level paging, trusting each operating system to
implement memory mapping and persistence better than a single library could
on its own.

Compaction
Realm automatically reuses free space that is no longer needed after database
writes. However, realm files never shrink automatically, even if the amount of
data stored in your realm decreases significantly. Compact your realm to
optimize storage space and decrease file size if possible.

You should compact your realms occasionally to keep them at an optimal size.
You can do this manually, or by configuring your realms to compact on
launch. However, Realm reclaims unused space for future writes, so
compaction is only an optimization to conserve space on-device.

ACID Compliance

Realm guarantees that transactions are ACID compliant. This means that all
committed write operations are guaranteed to be valid and that clients don't
see transient states in the event of a system crash. Realm complies with ACID
with the following design choices:

• Atomicity: groups operations in transactions and rolls back all


operations in a transaction if any of them fail.

• Consistency: avoids data corruption by validating changes against the


schema. If the result of any write operation is not valid, Realm cancels
and rolls back the entire transaction.

• Isolation: allows only one writer at a time. This ensures thread safety
between transactions.

• Durability: writes to disk immediately when a transaction is committed.


In the event of an app crash, for example, changes are not lost or
corrupted.

Features

Realm supports many popular database features.

Queries
You can query Realm using platform-native queries or a raw query language
that works across platforms.

Encryption

Realm supports on-device realm encryption. Since memory mapping does not
support encryption, encrypted realms use a simulated in-library form of
memory mapping instead.

NOTE

Realm forbids opening the same encrypted realm from multiple processes.
Attempting to do so will throw the error: "Encrypted interprocess sharing is
currently unsupported."

Indexes

Indexes are implemented as trees containing values of a given property


instead of a unique internal object key. This means that indexes only support
one column, and thus only one property, at a time.

Schemas

Every realm object has a schema. That schema is defined via a native object in
your SDK's language. Object schemas can include embedded lists and
relations between object instances.

Each realm uses a versioned schema. When that schema changes, you must
define a migration to move object data between schema versions. Non-
breaking schema changes, also referred to as additive schema changes, do
not require a migration. After you increment the local schema version, you can
begin using the updated schema in your app. Breaking schema changes, also
called destructive schema changes, require a migration function.

See your SDK's documentation for more information on migrations.


Persistent or In-Memory Realms

You can use Realm to store data persistently on disk, or ephemerally in


memory. Ephemeral realms can be useful in situations where you don't need
to persist data between application instances, such as when a user works in a
temporary workspace.

Atlas Device Sync

Device Sync adds network synchronization between an App Services backend


and client devices on top of all of the functionality of Realm. When you use
Realm with Sync, realms exist on device, similar to using Realm without Sync.
However, changes to the data stored in those realms synchronize between all
client devices through a backend App Services instance. That backend also
stores realm data in a cloud-based Atlas cluster running MongoDB.

Device Sync relies on a worker client that communicates with your application
backend in a dedicated thread in your application. Additionally, synced realms
keep a history of changes to contained objects. Sync uses this history to
resolve conflicts between client changes and backend changes.

Applications that use Device Sync define their schema on the backend
using JSON Schema. Client applications must match that backend schema to
synchronize data. However, if you prefer to define your initial schema in your
application's programming language, you can use Development Mode to
create a backend JSON Schema based on native SDK objects as you write
your application. However, once your application is used for production
purposes, you should alter your schema using JSON Schema on the backend.

Realm vs Other Databases

The Realm data model is similar to both relational and document databases
but has distinct differences from both. To underscore these differences, it's
helpful to highlight what a realm is not:
A realm is not a single, application-wide database.
Applications based on other database systems generally store all of their data in
a single database. Apps often split data across multiple realms to organize data
more efficiently and to enforce access controls.
A realm is not a relational table.
Normalized tables in relational databases only store one type of information,
such as street addresses or items in a store inventory. A realm can contain any
number of object types that are relevant to a given domain.
A realm is not a collection of schemaless documents.
Document databases don't necessarily enforce a strict schema for the data in
each collection. While similar to documents in form, every Realm object
conforms to a schema for a specific object type in the realm. An object cannot
contain a property that is not described by its schema.
Live Queries

You can query a realm to find objects based on their type and the
values of their properties. Objects and queries always reflect the
latest state of an object and emit notifications that can update your
app whenever data changes.

TIP

Learn How to Define and Run Queries

For code examples that show how to read and filter Realm objects
with the React Native SDK, see Read Operations.

Live Objects

Data in Realm is live, which means that an object always reflects


its most recent saved state and read operations never block.
Objects automatically update in response to changes, so you can
see up-to-date data in your application without running a new
query.

NOTE

Memory-mapped Realm Objects

Realm can support live objects because it memory-maps objects in


your application directly to data stored in the realm file instead of a
copy of the data stored in memory.

TIP

See also:

Learn how to read data from Realm.

Collections
A results collection represents all objects in a realm that match a
query operation. In general you can work with a collection like a
regular JavaScript array but collections don't actually hold
matching Realm objects in memory. Instead they reference the
matched objects, which themselves map directly to data in the
realm file.

NOTE

Pagination & Limits

Some queries only need to access a subset of all objects that


match the query. Realm's lazy-loaded collections only fetch
objects when you actually access them, so you do not need any
special mechanism to limit query results.

For example, if you only want to find 10 matching objects at a time


(such as in a paged product catalog) you can just access ten
elements of the results collection. To advance to the next page,
access the next ten elements of the results collection starting at
the index immediately following the last element of the previous
page.

Change Notifications

Realm objects and collections always reflect the latest state of


your data when you read them. Realm emits a change notification
whenever the state of your data changes, which lets you reactively
update your app in response to committed write transaction.

You can register three types of notification listeners:

• A realm listener fires whenever any object in a realm


changes.
• A collection listener fires whenever a specific query matches
a new set of objects or when any matched object changes.

• An object listener fires whenever a specific object is deleted


or has one or more properties modified.

TIP

Learn How to React to Changes

For code examples that show how to define, register, and clean up
change notification listeners with the React Native SDK, see React
to Changes.

TIP

See also:

To learn how to register change listeners, read the react to


changes documentation.
Use open-source Realm to store data on a device.

Install the Realm React Native SDK

Set up your project with React Native and Realm. To get started, install the
Realm React Native SDK.

Define an Object Schema

Use JavaScript to idiomatically define a Realm object schema.

Configure & Open a Realm

You can configure your realm to do things like populate initial data on load, be
encrypted, and more. To begin working with your data, configure and open a
realm.

Read and Write Data

You can create, read, update, and delete objects from a realm. Construct
complex queries to filter data in a realm.

React to Changes

Realm's live objects mean that your data is always up-to-date. Register a
change listener to react to changes and perform logic like updating your
UI.
Install Realm for React Native

Realm is now Atlas Device SDK – Learn More

The Realm React Native SDK enables development of React


Native applications using the JavaScript and TypeScript languages. React
Native enables you to build cross-platform iOS and Android apps with a single
codebase using the React framework.

Prerequisites

Before getting started, ensure your development environment meets the


following prerequisites. These are required for the latest version of the Realm
React Native SDK:

• Follow the official React Native CLI Quickstart instructions


to set up your environment.
• React Native v0.71.4 or later. Check out the compatibility chart
to determine which version of React Native is compatible with specific Realm
React Native SDK versions.

NOTE

Realm JS v10.6.0 and Later Support Mac Catalyst

For React Native version 0.64 and below, you must take additional steps
to build your application when using Mac Catalyst.

Using Realm with Expo

Expo supports Realm with the Expo SDK v48.

To use Realm with Expo, upgrade to Expo SDK v48. Check out
the compatibility chart to determine which version of the Expo SDK is
compatible with specific Realm React Native SDK versions.

Installation
Select the tab below that corresponds to your React Native version. Follow the
steps to create a React Native project and add the Realm React Native SDK to
it.
React Native v.60+
Older React Native Versions
1

Create a React Native Project

Create your React Native project with the following command:

npx react-native init MyRealmApp

Change to the project directory that the previous command just created:

cd MyRealmApp
2

Install Realm with npm

In your React Native project directory, add Realm to your project with the
following command:

npm install realm


3

Enable Hermes (optional)


NOTE

To use Hermes, your app must use Realm v11 or later and React Native 0.70.0
or later

Realm supports React Native's mobile-optimized JavaScript engine, Hermes.


By default, new apps created with the React Native CLI already have Hermes
enabled.

We recommend that you use Hermes with Realm. However, Realm also
supports the JavaScriptCore (JSC) engine if your app requires it.

Existing apps that currently use JSC can enable Hermes separately for
Android and iOS. To learn how, see the Using Hermes guide in the React
Native docs.

4
Resolve CocoaPods Dependencies

For the iOS app, fetch the CocoaPods dependencies with the following
commands from your React Native project directory:

cd ios && pod install && cd ..

This downloads the Realm libraries and regenerates the


project .xcworkspace file that you can work with in Xcode to run your
application.

Enable TypeScript (optional)

TypeScript is a superset of JavaScript that adds static type checking and other
features intended to make application-scale development more robust. If you'd
like to use TypeScript in your project, follow the React Native team's
official TypeScript and React Native guide. Realm supports TypeScript natively
and integrates easily into a TypeScript project.

Install the @realm/react Library

@realm/react is an npm package that provides an easy-to-use API to perform


common Realm operations, such as querying or writing to a realm and
listening to realm objects.

@realm/react helps you avoid creating boilerplate code, such as creating


your own listeners and state management. @realm/react provides access to
Realm through a set of hooks that update React state when the realm data
changes. This means that components using these hooks will re-render on any
changes to data in the realm.

NOTE

Using @realm/react with Realm JS Version 11


To use Realm JS version 11.0.0 or higher with @realm/react, you must
use @realm/react to version 0.4.0 or higher.

In your React Native project directory, add @realm/react to your project with
the following command:

npm install @realm/react


7

Run the App

React Native enables simultaneous development of both an iOS and Android


app that use the same React codebase. You can edit the .js or .ts source
files in your project directory to develop your app.

In development, the apps read their React source code as a bundle from a
local bundle server. To run the bundle server, use the following command in
your React Native project directory:

npm start

With the bundle server running, you can now launch the Android and iOS
apps:

• To run the Android app, use Android Studio to open


the android directory in your project directory and click Run.

• To run the iOS app, use Xcode to open the .xcworkspace file in
the ios directory. If you did not use CocoaPods during setup, open
the .xcodeproj file in the ios directory instead. Once you have
opened the project, click Run.

Import Realm

Add the following line to the top of your source files where you want to use
Realm:

import Realm from "realm";

Define an Object Type

To define a Realm object type, create a class that extends Realm.Object.


Define the type's name and properties in a static property called schema.
The type's name must be unique among object types in a realm.
TypeScript
JavaScript
class Book extends Realm.Object {
static schema = {
name: 'Book',
properties: {
name: {type: 'string', indexed: true},
price: 'int?',
},
};
}

Then you can pass the class itself to the schema property of
the Configuration object when opening a realm.

Supported Property Types

Every property in a Realm object has a strongly defined data type. A property's
type can be a primitive data type or an object type defined in the same realm.
The type also specifies whether the property contains a single value or a list of
values.

To specify that a field contains a list of a primitive value type, append [] to the
type name.

For a list of supported property types, see Property Types

Define Object Properties

To define a property for an object type, create a key-value pair representing


the name and data type of the property under the properties field.

The following schema defines a Car type that has these


properties: _id make, model, and miles.

Declare an Optional Property


To mark a property as optional, use object syntax and set optional to true.
You can also use a simplified syntax: append a question mark ? to the type.
This is best-suited to basic types. You should use the more specific object
syntax for more complicated types.

In the following example of a Person class, the age and birthday properties
are both optional.

class Person extends Realm.Object<Person> {


name!: string;
age?: number;
birthday?: Date;
static schema: ObjectSchema = {
name: 'Person',
properties: {
name: 'string',
age: {
type: 'int',
optional: true,
},
// You can use a simplified syntax instead. For
// more complicated types, use the object syntax.
birthday: 'date?',
},
};
}

Declare a Primary Key

To specify a property as an object type's primary key, set the


schema's primaryKey field to the property name.

NOTE

A primary key is a property that uniquely identifies an object. Realm


automatically indexes primary key properties, which allows you to read and
modify objects based on their primary key efficiently.

If an object type has a primary key, then all objects of that type must include
the primary key property with a unique value among objects of the same type
in a realm. An object type can have only one primary key. You cannot change
the primary key property for an object type after any object of that type is
added to a realm, and you cannot modify an object's primary key value.

In the following example of a Task class, we specify the _id property as the
primary key.

TypeScript
JavaScript
1 class Task extends Realm.Object {
2 static schema = {
3 name: 'Task',
4 properties: {
5 _id: 'int',
6 name: 'string',
7 priority: 'int?',
8 progressMinutes: 'int?',
9 assignee: 'Person?',
10 },
11 primaryKey: '_id',
12 };
13 }

Index a Property

If you frequently run read operations based on a specific property, you can
index the property to optimize performance. Realm supports indexing for
string, integer, boolean, Date, UUID, and ObjectId properties.

NOTE

An index significantly increases the speed of certain read operations at the


cost of slightly slower write times and additional storage and memory
overhead. Realm stores indexes on disk, which makes your realm files larger.
Each index entry is a minimum of 12 bytes. The ordering of the index entries
supports efficient equality matches and range-based query operations.

To index a given property, set the property's indexed field to true.

In the following example of a Book class, we define an index on


the name property.
TypeScript
JavaScript
1 class Book extends Realm.Object {
2 static schema = {
3 name: 'Book',
4 properties: {
5 name: {type: 'string', indexed: true},
6 price: 'int?',
7 },
8 };
9 }

Set a Full-Text Search Index

In addition to standard indexes, Realm also supports Full-Text Search (FTS)


indexes on string properties. While you can query a string field with or without
a standard index, an FTS index enables searching for multiple words and
phrases and excluding others.

For more information on querying FTS indexes, see Filter with Full-Text
Search.

To create an FTS index, set the indexed type to 'full-text'. This enables
full-text queries on the property. In the following example, we set the indexed
type for the name property to 'full-text':

class Book extends Realm.Object {


name!: string;
price?: number;
static schema: ObjectSchema = {
name: 'Book',
properties: {
name: {type: 'string', indexed: 'full-text'},
price: 'int?',
},
};
}

Set a Default Property Value

To define a default value, set the value of the property to an object with
a type field and a default field.
In the following example of a Car class, we define a miles property with a
default value of 0.

New in version 11.1.0.

In Realm.js v11.1.0 and later, you can use a function to define a dynamic
default value, like the timestamp property in the example below.

TypeScript
JavaScript
1 class Car extends Realm.Object {
2 static schema = {
3 name: 'Car',
4 properties: {
5 make: 'string',
6 model: 'string',
7 miles: {type: 'int', default: 0},
8 timestamp: {
9 type: 'int',
10 default: () => Math.round(new Date().getTime() / 1000),
11 },
12 },
13 };
14 }

Remap a Property

To use a different property name in your code than is stored in Realm,


set mapTo to the name of the property as it appears in your code.

In the following example of an Employee class, we remap


the first_name property to firstName.

TypeScript
JavaScript
1 class Employee extends Realm.Object {
2 static schema = {
3 name: 'Employee',
4 properties: {
5 _id: 'string',
6 first_name: {type: 'string', mapTo: 'firstName'},
7 },
8 primaryKey: '_id',
9 };
10 }
Define an Asymmetric Object

If you are using Flexible Sync and need to sync a collection unidirectionally
from your decive to your Atlas database, you can set
the asymmetric property on your object schema.

TypeScript
JavaScript
class WeatherSensor extends Realm.Object{
static schema = {
name: 'WeatherSensor',
// sync WeatherSensor objects one way from your device
// to your Atlas database.
asymmetric: true,
primaryKey: '_id',
properties: {
_id: 'objectId',
deviceId: 'string',
temperatureInFahrenheit: 'int',
barometricPressureInHg: 'float',
windSpeedInMph: 'float',
},
};
}

NOTE

Attempting to Read Asymmetric Objects

Asymmetric objects cannot be read. If you attempt to query an asymmetric


object, you will get the following error: "Error: You cannot query an
asymmetric class.".

To learn more about Data Ingest, read Stream Data to Atlas.

TypeScript and Required Properties

We recommend creating Realm objects with Realm.create(), but you can also
use the new operator for your object model's class.

If you use new, you must add your class as a generic, along with any required
properties, when extending Realm.Object. This enables full TypeScript
support for your object model, including type errors when required fields are
not defined.

class Book extends Realm.Object<Book, 'name' | 'store'> {


name!: string;
store!: string;
price?: number;
static schema = {
name: 'Book',
properties: {
name: {type: 'string', indexed: true},
store: 'string',
price: 'int?',
},
};
}

The Realm React Native SDK and @realm/react package provide many
configuration options for your realm.

How you configure your realm determines the capabilities of your realm and
how you work with your data. This page contains information about how to
configure your realm in various ways.
Prerequisites

Before you configure a realm in a React Native application:

1. Install the Realm React Native SDK

2. Install the @realm/react package

Configure a Realm Without Sync

RealmProvider is a wrapper that exposes a realm to its child components. You


configure your realm by passing props to RealmProvider.

When RealmProvider is rendered, it opens the realm. This means that the
provider renders successfully or its child components can't access the realm.

To configure a non-synced realm:

1. Import RealmProvider from @realm/react.

2. Pass your object models to the schema prop.

3. Add other Configuration object properties as props


to RealmProvider to configure your realm.
import React from 'react';
import {RealmProvider} from '@realm/react';
function AppWrapperLocal() {
return (
<RealmProvider schema={[YourObjectModel]}>
<RestOfApp />
</RealmProvider>
);
}

For a list of providers and hooks used in non-synced realm, check


out @realm/react Providers and Hooks.

Configuration Options
You can configure RealmProvider by setting props that match the properties
of a Configuration object. You can also set fallback and realmRef props.

• realmRef
Used with useRef to expose the configured realm to processes outside
of RealmProvider. This can be useful for things like a client reset fallback.

• fallback
Rendered while waiting for the realm to open. Local realms usually open fast
enough that the fallback prop isn't needed.

Configure an In-Memory Realm

To create a realm that runs entirely in memory without being written to a file,
pass true to the inMemory prop on your RealmProvider:

import React from 'react';


import {Realm, RealmProvider} from '@realm/react';
function AppWrapperLocal() {
return (
<RealmProvider inMemory={true}>
<RestOfApp />
</RealmProvider>
);
}

In-memory realms may use disk space if memory is running low, but files
created by an in-memory realm are deleted when you close the realm.

Encrypt a Realm

To encrypt a realm file on disk, refer to Encrypt a Realm.

Configure a Synced Realm

To open a realm that synchronizes data with Atlas using Device Sync, refer
to Open a Synced Realm.

Expose More Than One Realm


The @realm/react package exposes realms in your application using React
Context objects and Provider components. You can access realms with React
hooks.

To expose more than one realm, consider the following:

• Each realm needs its own Context object, created


with createRealmContext().

• The providers and hooks within each context should be namespaced so


that it's easy to reason about the realm you're working with.

• If you import RealmProvider directly from @realm/react, it is a


separate Context object. That object's providers and hooks can't be
unsynced with Context objects created using createRealmContext.

Create Separate Context Objects

You can open more than one realm at a time by creating additional Context
objects using createRealmContext().

TypeScript
JavaScript
import React from 'react';
import {
Realm,
AppProvider,
UserProvider,
createRealmContext,
} from '@realm/react';
class SharedDocument extends Realm.Object {
static schema = {
name: 'SharedDocument',
properties: {
_id: 'objectId',
owner_id: 'objectId',
title: 'string',
createdDate: 'date',
},
primaryKey: '_id',
};
}
class LocalDocument extends Realm.Object {
static schema = {
name: 'LocalDocument',
properties: {
_id: 'objectId',
name: 'string',
createdDate: 'date',
},
};
}
// Create Shared Document context object.
const SharedRealmContext = createRealmContext({
schema: [SharedDocument],
});
// Create Local Document context object.
const LocalRealmContext = createRealmContext({
schema: [LocalDocument],
});

Extract Providers and Hooks

You need to extract providers and hooks from each Context object. You
should namespace the providers and hooks using destructuring. This makes it
easier to reason about the realm you're working with.

Refer to Non-Synced RealmProvider Hooks to see which hooks are available


for a RealmProvider that isn't using Device Sync.

// Namespace the Shared Document context's providers and hooks.


const {
RealmProvider: SharedDocumentRealmProvider,
useRealm: useSharedDocumentRealm,
} = SharedRealmContext;
// Namespace the Local Document context's providers and hooks.
const {
RealmProvider: LocalDocumentRealmProvider,
useRealm: useLocalDocumentRealm,
} = LocalRealmContext;

Use Namespaced Providers and Hooks

After extracting a Context object's providers and hooks, you can use them in
your app's components. Child components inside of extracted providers have
access to extracted hooks.
function TwoRealmsWrapper() {
return (
<View>
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
{/* This is a Flexible Sync realm. */}
<SharedDocumentRealmProvider sync={{flexible: true}}>
<AppSectionOne />
</SharedDocumentRealmProvider>
</UserProvider>
</AppProvider>
{/* This is a separate local-only realm. */}
<LocalDocumentRealmProvider>
<AppSectionTwo />
</LocalDocumentRealmProvider>
</View>
);
}
function AppSectionOne() {
const realm = useSharedDocumentRealm();
// Work with shared documents...
}
function AppSectionTwo() {
const realm = useLocalDocumentRealm();
// Work with local documents...
}

Access a Realm Without Providing a Schema

After a realm has been created on a device, you don't need to always pass in a
schema to access the realm. Instead, you can use RealmProvider without
passing any object models to its schema property. The realm's schema is
derived from the existing realm file at Realm.defaultPath.

Accessing a realm without providing a schema only works for local realms.
You must always pass a schema when using a Synced realm.

import React from 'react';


import {RealmProvider} from '@realm/react';
function AppWrapper() {
return (
// To access a realm at the default path, do not pass any configuration.
// Requires a realm that has already been created.
<RealmProvider>
<RestOfApp />
</RealmProvider>
);
}

@realm/react Providers and Hooks

@realm/react has providers and hooks that simplify working with your non-
sync realm and its data.

Provider/Hook Description Example

RealmProviderA wrapper that exposes a realm to its child See Configure a Realm Without
components, which have access to hooks
that let you read, write, and update data.
useRealm Returns the instance of the Realm opened const realm = useRealm();
by the RealmProvider.
useObject Returns an object (Realm.Object<T>) from const myTask = useObject(Task,
a given type and value of primary key.
Updates on any changes to the returned
object. Returns null if the object either
doesn't exists or has been deleted.
useQuery Returns a collection of objects const tasks = useQuery(Task);
(Realm.Results<T & Realm.Object T>)
from a given type. Updates on any changes
to any object in the collection. Returns an
empty array if the collection is empty.

Realm Files

Realm stores a binary encoded version of every object and type in a realm in a
single .realm file. The file is located at a specific path that you define when
you open the realm.

TIP

Implement Compacting in Your Production Application

Every production application should implement


a shouldCompactOnLaunch callback to periodically reduce the realm file size.

NOTE
Auxiliary Realm Files

Realm creates additional files for each realm. To learn more about these files,
see Realm Internals.

WARNING

Use Caution When Deleting Realm Files

In some circumstances, such as a client reset scenario, you might need to


delete a realm file and its auxiliary files.

If you delete a realm file or any of its auxiliary files while one or more
instances of the realm are open, you might corrupt the realm or disrupt sync.

You may safely delete these files when all instances of a realm are closed.
Before you delete a realm file, make sure that you back up any important
objects as you will lose all unsynced data in the realm.

Encrypt a Realm - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can encrypt the realm database file on disk with AES-256 + SHA-2 by
supplying a 64-byte encryption key when opening a realm.

Realm transparently encrypts and decrypts data with standard AES-256


encryption using the first 256 bits of the given 512-bit encryption key. Realm
uses the other 256 bits of the 512-bit encryption key to validate integrity using
a hash-based message authentication code (HMAC).

WARNING

Do not use cryptographically-weak hashes for realm encryption keys. For


optimal security, we recommend generating random rather than derived
encryption keys.
Considerations

The following are key impacts to consider when encrypting a realm.

Storing & Reusing Keys

You must pass the same encryption key every time you open the encrypted
realm. If you don't provide a key or specify the wrong key for an encrypted
realm, the Realm SDK throws an error.

Apps should store the encryption key securely, typically in the target
platform's secure key/value storage, so that other apps cannot read the key.

Performance Impact

Reads and writes on encrypted realms can be up to 10% slower than


unencrypted realms.

Encryption and Atlas Device Sync

You can encrypt a synced realm.

Realm only encrypts the data on the device and stores the data unencrypted in
your Atlas data source. Any users with authorized access to the Atlas data
source can read the data, but the following still applies:

• Users must have the correct read permissions to read the synced data.

• Data stored in Atlas is always encrypted at a volume (disk) level.

• The transfer between client and server is always fully encrypted.

You can also enable Customer Key Management to encrypt stored Atlas data
using your cloud provider's key (e.g. AWS KMS, Azure Key Vault, Google
Cloud KMS).
If you need unique keys for each user of your application, you can use an
OAuth provider or use one of the Realm authentication providers and
an authentication trigger to create a 64-bit key and store that key in a user
object.

Accessing an Encrypted Realm from Multiple Processes

Changed in version [email protected] .

Starting with Realm React Native SDK version 11.8.0, Realm supports opening
the same encrypted realm in multiple processes.

If your app uses Realm React Native SDK version 11.7.0 or earlier, attempting
to open an encrypted realm from multiple processes throws this
error: Encrypted interprocess sharing is currently unsupported.

Example

The following code demonstrates how to generate an encryption key and open
an encrypted realm:

import React from 'react';


import {createRealmContext, Realm} from '@realm/react';
import Cat from '../Models/Cat';
import {FlatList, View} from 'react-native';
// Retrieve key from secure location or create one...
const key = new Int8Array(64); // Populate with a secure key
// ... store key ...
const config: Realm.Configuration = {
schema: [Cat.schema],
// Add encryption key to realm configuration
encryptionKey: key,
path: Date.now().toString() + '.realm', // :remove
};
const {RealmProvider, useQuery} = createRealmContext(config);
function App() {
return (
<RealmProvider>
<ListCats />
</RealmProvider>
);
}
// Work with realm as normal once it's been opened.
function ListCats() {
const cats = useQuery<Cat>('Cat');
return (
<FlatList
data={cats}
renderItem={({item}) => <View>{item.name}</View>}
keyExtractor={item => item.name}
/>
);

Quick Start - React Native SDK

Realm is now Atlas Device SDK – Learn More

This page demonstrates how to use Realm using the React Native SDK.

Before you begin, install the Realm React Native SDK.

About the @realm/react Package

@realm/react is a package used in the React Native SDK. It provides state-


aware React hooks for Realm data. The hooks watch the Realm data and re-
render components as needed.

The React Native SDK documentation uses the @realm/react npm package
for examples and describing concepts.

Set Up Your Realm App

After installing the realm and @realm/react packages, there are a few more
things to set up before you can access your realm and work with local data:

• Define your object models


• Configure a realm by creating a realm context object, extracting hooks,
and setting up providers

Define Object Models

Your application's object models define the data types that you can store
within a realm. Each object model becomes a Realm object type.

To define a Realm object model:

1. Create a class that extends Realm.Object. For TypeScript, include the


class name as a type. This lets you use the class with other Realm SDK
methods that accept Realm.Object types.

2. Add a schema field.

3. For the schema value, create an object that


contains properties and name properties. The name value must be
unique among object types in a Realm object model.

To learn more, refer to Define a Realm Object Model.

TypeScript
JavaScript
// Define your object model
class Profile extends Realm.Object {
static schema = {
name: 'Profile',
properties: {
_id: 'objectId',
name: 'string',
},
primaryKey: '_id',
};
}

Configure a Realm

Before you can work with data, you need to configure a realm. This means you
need to set up context and providers from @realm/react. To learn more, refer
to Configure a Realm.

To configure and access a local realm:

1. Import createRealmContext() from @realm/react.

2. Create a realm Configuration object. You must include your Realm


object models in the schema property. You can also include optional
configuration properties, like making the realm read only or adding Atlas
Device Sync.

3. Create a realm context with createRealmContext(). A realm context is


a React Context object that contains React providers and hooks for
working with your realm.

4. Expose a realm with RealmProvider. To expose a realm, you need a


realm context. From that context, extract RealmProvider, which
contains your realm's context.
TypeScript
JavaScript
import React from 'react';
import Realm from 'realm';
import {createRealmContext} from '@realm/react';
// Define your object model
class Profile extends Realm.Object {
static schema = {
name: 'Profile',
properties: {
_id: 'objectId',
name: 'string',
},
primaryKey: '_id',
};
}
// Create a configuration object
const realmConfig = {
schema: [Profile],
};
// Create a realm context
const {RealmProvider, useRealm, useObject, useQuery} =
createRealmContext(realmConfig);
// Expose a realm
function AppWrapper() {
return (
<RealmProvider>
<RestOfApp />
</RealmProvider>
);
}

Work With Realm Objects

After you have a data model and a configured realm, you can create, read,
update, or delete Realm objects.

You must nest any components that perform these operations inside of
a RealmProvider. The useRealm(), useQuery(), and useObject() hooks
enable you to perform read and write operations in your realm.
Find, Sort, and Filter Objects

@realm/react provides hooks to help you find a collection of Realm objects


or a single Realm object.

• useQuery(): Takes Realm.Object type as argument.


Returns Realm.Results with all objects in the realm for the type that you
pass to it.

• useObject(). Takes Realm.Object type and primary key as


arguments. Returns the Realm object for the primary key that you pass
to it.

After finding a collection, you can filter or sort the results using Realm Query
Language (RQL).

To learn more, refer to the Query Data.

TypeScript
JavaScript
import React, {useState} from 'react';
import Realm from 'realm';
import {createRealmContext} from '@realm/react';
// Define your object model
class Profile extends Realm.Object {
static schema = {
name: 'Profile',
properties: {
_id: 'objectId',
name: 'string',
},
primaryKey: '_id',
};
}
// Create a configuration object
const realmConfig = {
schema: [Profile],
};
// Create a realm context
const {RealmProvider, useObject, useQuery} = createRealmContext(realmConfig);
// Expose a realm
function AppWrapper() {
return (
<RealmProvider>
<FindSortFilterComponent objectPrimaryKey={YOUR_PRIMARY_KEY} />
</RealmProvider>
);
}
const FindSortFilterComponent = ({objectPrimaryKey}) => {
const [activeProfile, setActiveProfile] = useState();
const [allProfiles, setAllProfiles] = useState();

const currentlyActiveProfile = useObject(Profile, objectPrimaryKey);


const profiles = useQuery(Profile);

const sortProfiles = (reversed) => {


const sorted = profiles.sorted('name', reversed);
setAllProfiles(sorted);
};
const filterProfiles = (filter, letter) => {
// Use [c] for case-insensitivity.
const filtered = profiles.filtered(`name ${filter}[c] "${letter}"`);
setAllProfiles(filtered);
};
// ... rest of component
};

Create, Modify, and Delete Realm Objects

After accessing the realm with useRealm(), you can create, modify, and
delete objects inside of the realm in a Realm.write() transaction block.

To learn more, refer to Write Transactions.

TypeScript
JavaScript
import React from 'react';
import Realm from 'realm';
import {createRealmContext} from '@realm/react';
// Define your object model
class Profile extends Realm.Object {
static schema = {
name: 'Profile',
properties: {
_id: 'objectId',
name: 'string',
},
primaryKey: '_id',
};
}
// Create a configuration object
const realmConfig = {
schema: [Profile],
};
// Create a realm context
const {RealmProvider, useRealm, useObject, useQuery} =
createRealmContext(realmConfig);
// Expose a realm
function AppWrapper() {
return (
<RealmProvider>
<RestOfApp objectPrimaryKey={YOUR_PRIMARY_KEY} />
</RealmProvider>
);
}
function RestOfApp({objectPrimaryKey}) {
const [selectedProfileId, setSelectedProfileId] = useState(objectPrimaryKey);

const realm = useRealm();

const changeProfileName = (profileToChange, newName) => {

realm.write(() => {
profileToChange.name = newName;
});

};
// ... rest of component
}

Create Objects

To create a new Realm object, specify the object type, pass in the object's
initial values, and add it to the realm in a write transaction block.

To learn more, refer to CRUD - Create.

TypeScript
JavaScript
const addProfile = (name) => {
realm.write(() => {
realm.create('Profile', {
name: name,
_id: new Realm.BSON.ObjectId(),
});
});
};

Update Objects

To update a Realm Object, update its properties in a write transaction block.

To learn more, refer to CRUD - Update.

TypeScript
JavaScript
const changeProfileName = (profile, newName) => {
realm.write(() => {
profile.name = newName;
});
};

Delete Objects

To delete a Realm Object, call the Realm.delete() method in a write transaction


block.

To learn more, refer to CRUD - Delete.

TypeScript
JavaScript
const deleteProfile = (profile) => {
realm.write(() => {
realm.delete(profile);
});
};

Add Atlas Device Sync (Optional)

After getting your local-only realm running, you can add Atlas Device Sync so
that your realm's data can sync with a MongoDB Atlas cluster and other client
devices.

To use Device Sync, you need to set up a couple more things:

• Create a backend in Atlas App Services (see the prerequisites below)

• Configure a Flexible Sync realm instead of a local-only realm

Prerequisites

• An App Services App

• Anonymous authentication enabled in the App Services UI

• Flexible Sync enabled with Development Mode on

Configure and Access a Synced Realm

To configure and access a synced realm:

1. Initialize the App using AppProvider

2. Authenticate a User with UserProvider

3. Configure a Synced Realm with RealmProvider

To learn more, refer to Configure a Synced Realm.


Initialize the App using AppProvider

To use App Services features, such as authentication and Device Sync, you
must first access your App Services App using your App ID. You can find your
App ID in the App Services UI.

import React from 'react';


import {AppProvider} from '@realm/react';
function AppWrapperSync() {
return (

<AppProvider id={APP_ID}>
<RestOfApp />
</AppProvider>

);
}

Authenticate a User with UserProvider

Use the UserProvider to handle sections of your app that need an


authenticated user.

To authenticate and log in a user, provide a fallback prop


for UserProvider. This could be a log in screen component or a simple
function that calls App.logIn().

Users can only access the parts of your app nested


within UserProvider after they have authenticated. If there's no authenticated
user, the fallback component renders and nested components do not.

import React from 'react';


import {useApp, AppProvider, UserProvider} from '@realm/react';
import {Button} from 'react-native';
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>

<UserProvider fallback={LogIn}>
<RestOfApp />
</UserProvider>

</AppProvider>
);
}
function LogIn() {
const app = useApp();
async function logInUser() {
// When anonymous authentication is enabled, users can immediately log
// into your app without providing any identifying information.
await app.logIn(Realm.Credentials.anonymous());
}
return (
<Button
title='Log In'
onPress={logInUser}
/>
);
}

Configure a Synced Realm with RealmProvider

After you have initialized your App, authenticated a user, and defined your
object model, you can configure a synced realm. This is similar to configuring
a local realm. However, you need to add some additional props to
the RealmProvider.
1. Create the realm's Configuration object. The Configuration object
defines the parameters of a realm and identifies it. When creating a
configuration object, make sure to pass your data models into
the schema property.

2. Create a realm context with createRealmContext(). A realm context is


a React Context object that contains app-wide access to your realm.

3. Extract the RealmProvider from the realm context, and then expose it
to your app.

4. Add the sync property to RealmProvider and pass it


a FlexibleSyncConfiguration object. This sync object must
contain flexible: true.

You need at least one sync subscription before you can read or write synced
data. You can add subscriptions in your components or set up initial
subscriptions on RealmProvider.

TypeScript
JavaScript
import React from 'react';
import Realm from 'realm';
import {AppProvider, UserProvider, createRealmContext} from '@realm/react';
// Define your object model
class Profile extends Realm.Object {
static schema = {
name: 'Profile',
properties: {
_id: 'objectId',
name: 'string',
},
primaryKey: '_id',
};
}
// Create a configuration object
const realmConfig = {
schema: [Profile],
};
// Create a realm context
const {RealmProvider, useRealm, useObject, useQuery} =
createRealmContext(realmConfig);
// Expose a sync realm
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>

<RealmProvider
sync={{
flexible: true,
onError: console.error,
initialSubscriptions: {
update(subs, realm) {
subs.add(realm.objects('Profile'));
},
},
}}>
<RestOfApp />
</RealmProvider>

</UserProvider>
</AppProvider>
);
}
The syntax to create, read, update, and delete objects in a synced realm is
identical to the syntax for non-synced realms. While you work with local data,
a background thread efficiently integrates, uploads, and downloads
changesets.

To learn more, refer to Configure a Synced Realm.

Next: Check out the Template Apps and Tutorial

If you are interested in a guided experience, you can read our Realm React
Native SDK tutorial. This tutorial implements and expands on a base React
Native app built with Realm and Device Sync.

You could also use the template app to experiment with the React Native SDK
on your own. To set up the template app, refer to Template Apps in the Atlas
App Services documentation.

Work with Realm Files - React Native SDK

Realm is now Atlas Device SDK – Learn More

Realms are the core data structure used to organize data in Realm Database.
At its core, a realm is a collection of the objects that you use in your
application, called Realm objects, as well as additional metadata that describe
the objects.

Realm Files

Realm stores a binary encoded version of every object and type in a realm in a
single .realm file. The file is located at a specific path that you define when
you open the realm.

TIP

Implement Compacting in Your Production Application


Every production application should implement
a shouldCompactOnLaunch callback to periodically reduce the realm file size.

NOTE

Auxiliary Realm Files

Realm creates additional files for each realm.

WARNING

Use Caution When Deleting Realm Files

In some circumstances, such as a client reset scenario, you might need to


delete a realm file and its auxiliary files.

If you delete a realm file or any of its auxiliary files while one or more
instances of the realm are open, you might corrupt the realm or disrupt sync.

You may safely delete these files when all instances of a realm are closed.
Before you delete a realm file, make sure that you back up any important
objects as you will lose all unsynced data in the realm.

In-Memory Realms

You can also open a realm entirely in memory, which will not create
a .realm file or its associated auxiliary files. Instead the SDK stores objects in
memory while the realm is open and discards them immediately when all
instances are closed.

Find a Realm File Path

The realm file is located at a specific path that you can optionally define when
you open the realm.

// Open a realm.
const realm = await Realm.open({
schema: [Car],
});
// Get on-disk location of the Realm
const realmFileLocation = realm.path;
console.log(`Realm file is located at: ${realm.path}`);

Configure a Realm - React Native SDK

Realm is now Atlas Device SDK – Learn More

The Realm React Native SDK and @realm/react package provide many
configuration options for your realm.

How you configure your realm determines the capabilities of your realm and
how you work with your data. This page contains information about how to
configure your realm in various ways.

Prerequisites

Before you configure a realm in a React Native application:

1. Install the Realm React Native SDK

2. Install the @realm/react package

Configure a Realm Without Sync

RealmProvider is a wrapper that exposes a realm to its child components. You


configure your realm by passing props to RealmProvider.

When RealmProvider is rendered, it opens the realm. This means that the
provider renders successfully or its child components can't access the realm.

To configure a non-synced realm:

1. Import RealmProvider from @realm/react.

2. Pass your object models to the schema prop.


3. Add other Configuration object properties as props
to RealmProvider to configure your realm.
import React from 'react';
import {RealmProvider} from '@realm/react';
function AppWrapperLocal() {
return (
<RealmProvider schema={[YourObjectModel]}>
<RestOfApp />
</RealmProvider>
);
}

For a list of providers and hooks used in non-synced realm, check


out @realm/react Providers and Hooks.

Configuration Options

You can configure RealmProvider by setting props that match the properties
of a Configuration object. You can also set fallback and realmRef props.

• realmRef
Used with useRef to expose the configured realm to processes outside
of RealmProvider. This can be useful for things like a client reset fallback.

• fallback
Rendered while waiting for the realm to open. Local realms usually open fast
enough that the fallback prop isn't needed.

Configure an In-Memory Realm

To create a realm that runs entirely in memory without being written to a file,
pass true to the inMemory prop on your RealmProvider:

import React from 'react';


import {Realm, RealmProvider} from '@realm/react';
function AppWrapperLocal() {
return (
<RealmProvider inMemory={true}>
<RestOfApp />
</RealmProvider>
);
}
In-memory realms may use disk space if memory is running low, but files
created by an in-memory realm are deleted when you close the realm.

Encrypt a Realm

To encrypt a realm file on disk, refer to Encrypt a Realm.

Configure a Synced Realm

To open a realm that synchronizes data with Atlas using Device Sync, refer
to Open a Synced Realm.

Expose More Than One Realm

The @realm/react package exposes realms in your application using React


Context objects and Provider components. You can access realms with React
hooks.

To expose more than one realm, consider the following:

• Each realm needs its own Context object, created


with createRealmContext().

• The providers and hooks within each context should be namespaced so


that it's easy to reason about the realm you're working with.

• If you import RealmProvider directly from @realm/react, it is a


separate Context object. That object's providers and hooks can't be
unsynced with Context objects created using createRealmContext.

Create Separate Context Objects

You can open more than one realm at a time by creating additional Context
objects using createRealmContext().

TypeScript
JavaScript
import React from 'react';
import {
Realm,
AppProvider,
UserProvider,
createRealmContext,
} from '@realm/react';
class SharedDocument extends Realm.Object {
static schema = {
name: 'SharedDocument',
properties: {
_id: 'objectId',
owner_id: 'objectId',
title: 'string',
createdDate: 'date',
},
primaryKey: '_id',
};
}
class LocalDocument extends Realm.Object {
static schema = {
name: 'LocalDocument',
properties: {
_id: 'objectId',
name: 'string',
createdDate: 'date',
},
};
}
// Create Shared Document context object.
const SharedRealmContext = createRealmContext({
schema: [SharedDocument],
});
// Create Local Document context object.
const LocalRealmContext = createRealmContext({
schema: [LocalDocument],
});

Extract Providers and Hooks

You need to extract providers and hooks from each Context object. You
should namespace the providers and hooks using destructuring. This makes it
easier to reason about the realm you're working with.

Refer to Non-Synced RealmProvider Hooks to see which hooks are available


for a RealmProvider that isn't using Device Sync.
// Namespace the Shared Document context's providers and hooks.
const {
RealmProvider: SharedDocumentRealmProvider,
useRealm: useSharedDocumentRealm,
} = SharedRealmContext;
// Namespace the Local Document context's providers and hooks.
const {
RealmProvider: LocalDocumentRealmProvider,
useRealm: useLocalDocumentRealm,
} = LocalRealmContext;

Use Namespaced Providers and Hooks

After extracting a Context object's providers and hooks, you can use them in
your app's components. Child components inside of extracted providers have
access to extracted hooks.

function TwoRealmsWrapper() {
return (
<View>
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
{/* This is a Flexible Sync realm. */}
<SharedDocumentRealmProvider sync={{flexible: true}}>
<AppSectionOne />
</SharedDocumentRealmProvider>
</UserProvider>
</AppProvider>
{/* This is a separate local-only realm. */}
<LocalDocumentRealmProvider>
<AppSectionTwo />
</LocalDocumentRealmProvider>
</View>
);
}
function AppSectionOne() {
const realm = useSharedDocumentRealm();
// Work with shared documents...
}
function AppSectionTwo() {
const realm = useLocalDocumentRealm();
// Work with local documents...
}

Access a Realm Without Providing a Schema


After a realm has been created on a device, you don't need to always pass in a
schema to access the realm. Instead, you can use RealmProvider without
passing any object models to its schema property. The realm's schema is
derived from the existing realm file at Realm.defaultPath.

Accessing a realm without providing a schema only works for local realms.
You must always pass a schema when using a Synced realm.

import React from 'react';


import {RealmProvider} from '@realm/react';
function AppWrapper() {
return (
// To access a realm at the default path, do not pass any configuration.
// Requires a realm that has already been created.
<RealmProvider>
<RestOfApp />
</RealmProvider>
);
}

@realm/react Providers and Hooks

@realm/react has providers and hooks that simplify working with your non-
sync realm and its data.

Provider/Hook Description Example

RealmProviderA wrapper that exposes a realm to its child See Configure a Realm Without
components, which have access to hooks
that let you read, write, and update data.
useRealm Returns the instance of the Realm opened const realm = useRealm();
by the RealmProvider.
useObject Returns an object (Realm.Object<T>) from const myTask = useObject(Task,
a given type and value of primary key.
Updates on any changes to the returned
object. Returns null if the object either
doesn't exists or has been deleted.
useQuery Returns a collection of objects const tasks = useQuery(Task);
(Realm.Results<T & Realm.Object T>)
from a given type. Updates on any changes
Provider/Hook Description Example

to any object in the collection. Returns an


empty array if the collection is empty.

Realm Files

Realm stores a binary encoded version of every object and type in a realm in a
single .realm file. The file is located at a specific path that you define when
you open the realm.

TIP

Implement Compacting in Your Production Application

Every production application should implement


a shouldCompactOnLaunch callback to periodically reduce the realm file size.

NOTE

Auxiliary Realm Files

Realm creates additional files for each realm. To learn more about these files,
see Realm Internals.

WARNING

Use Caution When Deleting Realm Files

In some circumstances, such as a client reset scenario, you might need to


delete a realm file and its auxiliary files.

If you delete a realm file or any of its auxiliary files while one or more
instances of the realm are open, you might corrupt the realm or disrupt sync.

You may safely delete these files when all instances of a realm are closed.
Before you delete a realm file, make sure that you back up any important
objects as you will lose all unsynced data in the realm.
Bundle a Realm File - React Native SDK

Realm is now Atlas Device SDK – Learn More

Realm supports bundling realm files. When you bundle a realm file, you
include a database and all of its data in your application download.

This allows users to start applications for the first time with a set of initial data.
For synced realms, bundling can avoid a lengthy initial download the first time
a user opens your application. Instead, users must only download the synced
changes that occurred since you generated the bundled file.

IMPORTANT

Only Applies to Local Realms

The content on this page only applies to local realms.

WARNING

Does Not Apply to Expo Apps

This procedure doesn't work for React Native apps created with Expo.

Procedure

Follow these steps to create and bundle a realm file for your React Native
application.

Create a Realm File to Bundle

The easiest way to create a bundled realm for your React Native app is to write
a separate Node.js script to create the bundle.

You should use the realm package to create your bundle rather
than @realm/react.
1. Build a temporary realm app that shares the data model of your
application.

2. Open a realm and add the data you wish to bundle. If using a
synchronized realm, allow time for the realm to fully sync.

create-bundled-realm.js

import Realm from "realm";


import { Dog } from "./schemas";
// open realm
const config = {
schema: [Dog],
path: "bundle.realm",
};
const realm = await Realm.open(config);
// add data to realm
realm.write(() => {
realm.create("Dog", { name: "Jasper", age: 10, type: "Golden Retriever" });
realm.create("Dog", { name: "Maggie", age: 12, type: "Collie" });
realm.create("Dog", { name: "Sophie", age: 6, type: "German Shepard" });
});
realm.close();

3. Note the filepath of the bundled realm file. You'll need this file to use the
bundled realm in your production application, as described in the next
section.

temp_realm_app

.
├── bundle.realm
... rest of files in _temp_ application
2

Bundle the Realm File with your App

Now that you have a copy of the realm that contains the initial data, add the
bundled realm file to your production application. Where you place the
bundled realm differs for iOS and Android builds.

Android
iOS
1. Open up the android folder generated by React Native in Android
Studio.

2. In the Project tree, navigate to app > src > main. Right click
the main directory. Create a new subdirectory named assets.

3. Drag the bundled realm file into the assets directory.


3

Open the Bundled Realm in your App

The realm is now bundled and will be included when a user downloads the
app. To add the bundled realm file to your app's document directory,
call Realm.copyBundledRealmFiles() before you open the realm.

Realm.copyBundledRealmFiles() adds all *.realm files from the


application bundle to the application documents directory. This method
doesn't override any existing files with the same name, so it's safe to call every
time the app starts.

Open the bundled realm with the same name and configuration that you
specified when you initially created the bundled realm.

Now that you have a copy of the realm included with your production
application, you need to add code to use it.

import React from 'react';


import {createRealmContext, Realm} from '@realm/react';
import {Dog} from './schema';
Realm.copyBundledRealmFiles();
const realmContext = createRealmContext({schema: [Dog], path: 'bundle.realm'});
const {RealmProvider} = realmContext;
export default function OpenBundledRealm() {
return (
<RealmProvider>
{/* Rest of app has access to objects pre-populated
in the bundled realm. */}
<RestOfApp />
</RealmProvider>
);
}
Encrypt a Realm - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can encrypt the realm database file on disk with AES-256 + SHA-2 by
supplying a 64-byte encryption key when opening a realm.

Realm transparently encrypts and decrypts data with standard AES-256


encryption using the first 256 bits of the given 512-bit encryption key. Realm
uses the other 256 bits of the 512-bit encryption key to validate integrity using
a hash-based message authentication code (HMAC).

WARNING

Do not use cryptographically-weak hashes for realm encryption keys. For


optimal security, we recommend generating random rather than derived
encryption keys.

Considerations

The following are key impacts to consider when encrypting a realm.

Storing & Reusing Keys

You must pass the same encryption key every time you open the encrypted
realm. If you don't provide a key or specify the wrong key for an encrypted
realm, the Realm SDK throws an error.

Apps should store the encryption key securely, typically in the target
platform's secure key/value storage, so that other apps cannot read the key.

Performance Impact

Reads and writes on encrypted realms can be up to 10% slower than


unencrypted realms.
Encryption and Atlas Device Sync

You can encrypt a synced realm.

Realm only encrypts the data on the device and stores the data unencrypted in
your Atlas data source. Any users with authorized access to the Atlas data
source can read the data, but the following still applies:

• Users must have the correct read permissions to read the synced data.

• Data stored in Atlas is always encrypted at a volume (disk) level.

• The transfer between client and server is always fully encrypted.

You can also enable Customer Key Management to encrypt stored Atlas data
using your cloud provider's key (e.g. AWS KMS, Azure Key Vault, Google
Cloud KMS).

If you need unique keys for each user of your application, you can use an
OAuth provider or use one of the Realm authentication providers and
an authentication trigger to create a 64-bit key and store that key in a user
object.

Accessing an Encrypted Realm from Multiple Processes

Changed in version [email protected] .

Starting with Realm React Native SDK version 11.8.0, Realm supports opening
the same encrypted realm in multiple processes.

If your app uses Realm React Native SDK version 11.7.0 or earlier, attempting
to open an encrypted realm from multiple processes throws this
error: Encrypted interprocess sharing is currently unsupported.

Example
The following code demonstrates how to generate an encryption key and open
an encrypted realm:

import React from 'react';


import {createRealmContext, Realm} from '@realm/react';
import Cat from '../Models/Cat';
import {FlatList, View} from 'react-native';
// Retrieve key from secure location or create one...
const key = new Int8Array(64); // Populate with a secure key
// ... store key ...
const config: Realm.Configuration = {
schema: [Cat.schema],
// Add encryption key to realm configuration
encryptionKey: key,
path: Date.now().toString() + '.realm', // :remove
};
const {RealmProvider, useQuery} = createRealmContext(config);
function App() {
return (
<RealmProvider>
<ListCats />
</RealmProvider>
);
}
// Work with realm as normal once it's been opened.
function ListCats() {
const cats = useQuery<Cat>('Cat');
return (
<FlatList
data={cats}
renderItem={({item}) => <View>{item.name}</View>}
keyExtractor={item => item.name}
/>
);
}

Reduce Realm File Size - React Native SDK

Realm is now Atlas Device SDK – Learn More

Over time, the storage space used by Realm might become fragmented and
take up more space than necessary. To rearrange the internal storage and
potentially reduce the file size, the realm file needs to be compacted.
Realm's default behavior is to automatically compact a realm file to prevent it
from growing too large. You can use manual compaction strategies when
automatic compaction is not sufficient for your use case.

Automatic Compaction

New in version 11.3.0.

The SDK automatically compacts Realm files in the background by


continuously reallocating data within the file and removing unused file space.
Automatic compaction is sufficient for minimizing the Realm file size for most
applications.

Automatic compaction begins when the size of unused space in the file is
more than twice the size of user data in the file. Automatic compaction only
takes place when the file is not being accessed.

Manual Compaction Options

Manual compaction can be used for applications that require stricter


management of file size or that use an older version of the SDK that does not
support automatic compaction.

Realm reduces the file size by writing a new (compact) version of the file and
then replacing the original with the newly-written file. Therefore, to compact,
you must have free storage space equivalent to the original realm file size.

You can configure Realm to automatically compact the database each time a
realm is opened, or you can compact the file without first obtaining a realm
instance.

Realm Configuration File


You can configure Realm to check the realm file each time it is opened by
specifying a shouldCompact function for the configuration. The following code
example shows how to do this:

const shouldCompact = (totalBytes, usedBytes) => {


// totalBytes refers to the size of the file on disk in bytes (data + free space)
// usedBytes refers to the number of bytes used by data in the file
// Compact if the file is over 100MB in size and less than 50% 'used'
const oneHundredMB = 100 * 1024 * 1024;
return totalBytes > oneHundredMB && usedBytes / totalBytes < 0.5;
};
const config = { shouldCompact };
let realm = await Realm.open(config);

Realm.compact() Method

Alternatively, you can compact a realm file without first obtaining an instance
of the realm by calling the compact() method:

const realm = new Realm("my.realm");


realm.compact();

The compact() method will return true if the operation is successful.

Make a Compacted Copy

You can save a compacted (and optionally encrypted) copy of a realm to


another file location with the Realm.writeCopyTo() method. The destination file
cannot already exist.

IMPORTANT

Avoid calling writeCopyTo() within a write transaction. If called within a write


transaction, this method copies the absolute latest data. This includes
any uncommitted changes you made in the transaction before this method
call.

Tips for Manually Compacting a Realm


Manually compacting a realm can be a resource-intensive operation. Your
application should not compact every time you open a realm. Instead, try to
optimize compacting so your application does it just often enough to prevent
the file size from growing too large. If your application runs in a resource-
constrained environment, you may want to compact when you reach a certain
file size or when the file size negatively impacts performance.

These recommendations can help you start optimizing compaction for your
application:

• Set the max file size to a multiple of your average realm state size. If
your average realm state size is 10MB, you might set the max file size to
20MB or 40MB, depending on expected usage and device constraints.

• As a starting point, compact realms when more than 50% of the realm
file size is no longer in use. Divide the currently used bytes by the total
file size to determine the percentage of space that is currently used.
Then, check for that to be less than 50%. This means that greater than
50% of your realm file size is unused space, and it is a good time to
compact. After experimentation, you may find a different percentage
works best for your application.

These calculations might look like this:

// Set a maxFileSize equal to 20MB in bytes


const maxFileSize = 20 * 1024 * 1024;
/* Check for the realm file size to be greater than the max file size,
* and the amount of bytes currently used to be less than 50% of the
* total realm file size */
return (totalBytes > (double)maxFileSize) ||
((double)usedBytes / totalBytes > 0.5);

Experiment with conditions to find the right balance of how often to compact
realm files in your application.

Model Data - React Native SDK

Realm is now Atlas Device SDK – Learn More


Every Realm object conforms to a specific object type. Object types are
classes you define that contain the properties and relationships for objects of
that type using a pre-defined schema.

Realm guarantees that all objects in a realm conform to the schema for their
object type and validates objects whenever they're created, modified, or
deleted.

Realm objects are fundamentally similar to a common JavaScript object but


they also bring along a few additional features like schema validation and live
queries.

The React Native SDK memory maps Realm objects directly to native
JavaScript objects, which means there's no need to use a special data access
library, such as an ORM. Instead, you can work with Realm objects as you
would any other object.

Realm Schema

A realm schema is a list of valid object schemas that a realm may contain.
Every Realm object must conform to an object type that's included in its
realm's schema.

If a realm already contains data when you open it, Realm validates each object
to ensure that an object schema was provided for its type and that it meets all
of the constraints specified in the schema.

Using @realm/react, you define a realm schema by passing individual object


schemas to RealmProvider or createRealmContext().

import Profile from './Models/Profile';


import {createRealmContext} from '@realm/react';
export const SyncedRealmContext = createRealmContext({
// Pass all of your models into the schema value.
schema: [Profile],
});
Relationships

You can define relationships between objects in a realm. Realm models


relationships as object properties that point to other objects of a given type in
the realm. You define a relationship at the type level by declaring a property in
the type's schema where the value is another object type.

Querying a relationship is just as performant as a regular property.


Relationships are direct references to other objects, so you don't need to use
joins and complex models to define and use them like you would in a relational
database. Instead, you can access related objects by reading and writing to
the relationship property directly.

There are three primary types of relationships between objects:

• One-to-One Relationship

• One-to-Many Relationship

• Inverse Relationship

NOTE

Realm vs Other Databases

Objects often contain direct references to other objects. When working with
objects and references, you typically copy data from database storage into
application memory. This situation leaves the developer with a choice of what
to copy into memory:

• You can copy all referenced objects into memory ahead of time. This
means that all referenced data is always available quickly but can use
up a lot of resources. If a system has limited memory, this may not be
viable.

• You can copy just a foreign key value for each object. Later, you can use
the key to look up the full object when you need it. These "lazy" lookups
are more memory-efficient than copying all referenced objects ahead of
time. However, they require you to maintain more query code and use
runtime lookups that can slow your app down.

Realm's query architecture avoids the tradeoff between memory usage and
computational overhead. Instead, Realm queries can directly reference related
objects and their properties on disk.

TIP

See also:

Alternatively, you can define your relationships in your App Services app.

Define a Realm Object Model - React Native SDK

Realm is now Atlas Device SDK – Learn More

Define an Object Type

To define a Realm object type, create a class that extends Realm.Object.


Define the type's name and properties in a static property called schema.
The type's name must be unique among object types in a realm.

TypeScript
JavaScript
class Book extends Realm.Object {
static schema = {
name: 'Book',
properties: {
name: {type: 'string', indexed: true},
price: 'int?',
},
};
}

Then you can pass the class itself to the schema property of
the Configuration object when opening a realm.
Supported Property Types

Every property in a Realm object has a strongly defined data type. A property's
type can be a primitive data type or an object type defined in the same realm.
The type also specifies whether the property contains a single value or a list of
values.

To specify that a field contains a list of a primitive value type, append [] to the
type name.

For a list of supported property types, see Property Types

Define Object Properties

To define a property for an object type, create a key-value pair representing


the name and data type of the property under the properties field.

The following schema defines a Car type that has these


properties: _id make, model, and miles.

Declare an Optional Property

To mark a property as optional, use object syntax and set optional to true.
You can also use a simplified syntax: append a question mark ? to the type.
This is best-suited to basic types. You should use the more specific object
syntax for more complicated types.

In the following example of a Person class, the age and birthday properties
are both optional.

class Person extends Realm.Object<Person> {


name!: string;
age?: number;
birthday?: Date;
static schema: ObjectSchema = {
name: 'Person',
properties: {
name: 'string',
age: {
type: 'int',
optional: true,
},
// You can use a simplified syntax instead. For
// more complicated types, use the object syntax.
birthday: 'date?',
},
};
}

Declare a Primary Key

To specify a property as an object type's primary key, set the


schema's primaryKey field to the property name.

NOTE

A primary key is a property that uniquely identifies an object. Realm


automatically indexes primary key properties, which allows you to read and
modify objects based on their primary key efficiently.

If an object type has a primary key, then all objects of that type must include
the primary key property with a unique value among objects of the same type
in a realm. An object type can have only one primary key. You cannot change
the primary key property for an object type after any object of that type is
added to a realm, and you cannot modify an object's primary key value.

In the following example of a Task class, we specify the _id property as the
primary key.

TypeScript
JavaScript
1 class Task extends Realm.Object {
2 static schema = {
3 name: 'Task',
4 properties: {
5 _id: 'int',
6 name: 'string',
7 priority: 'int?',
8 progressMinutes: 'int?',
9 assignee: 'Person?',
10 },
11 primaryKey: '_id',
12 };
13 }

Index a Property

If you frequently run read operations based on a specific property, you can
index the property to optimize performance. Realm supports indexing for
string, integer, boolean, Date, UUID, and ObjectId properties.

NOTE

An index significantly increases the speed of certain read operations at the


cost of slightly slower write times and additional storage and memory
overhead. Realm stores indexes on disk, which makes your realm files larger.
Each index entry is a minimum of 12 bytes. The ordering of the index entries
supports efficient equality matches and range-based query operations.

To index a given property, set the property's indexed field to true.

In the following example of a Book class, we define an index on


the name property.

TypeScript
JavaScript
1 class Book extends Realm.Object {
2 static schema = {
3 name: 'Book',
4 properties: {
5 name: {type: 'string', indexed: true},
6 price: 'int?',
7 },
8 };
9 }

Set a Full-Text Search Index

In addition to standard indexes, Realm also supports Full-Text Search (FTS)


indexes on string properties. While you can query a string field with or without
a standard index, an FTS index enables searching for multiple words and
phrases and excluding others.

For more information on querying FTS indexes, see Filter with Full-Text
Search.

To create an FTS index, set the indexed type to 'full-text'. This enables
full-text queries on the property. In the following example, we set the indexed
type for the name property to 'full-text':

class Book extends Realm.Object {


name!: string;
price?: number;
static schema: ObjectSchema = {
name: 'Book',
properties: {
name: {type: 'string', indexed: 'full-text'},
price: 'int?',
},
};
}

Set a Default Property Value

To define a default value, set the value of the property to an object with
a type field and a default field.

In the following example of a Car class, we define a miles property with a


default value of 0.

New in version 11.1.0.

In Realm.js v11.1.0 and later, you can use a function to define a dynamic
default value, like the timestamp property in the example below.

TypeScript
JavaScript
1 class Car extends Realm.Object {
2 static schema = {
3 name: 'Car',
4 properties: {
5 make: 'string',
6 model: 'string',
7 miles: {type: 'int', default: 0},
8 timestamp: {
9 type: 'int',
10 default: () => Math.round(new Date().getTime() / 1000),
11 },
12 },
13 };
14 }

Remap a Property

To use a different property name in your code than is stored in Realm,


set mapTo to the name of the property as it appears in your code.

In the following example of an Employee class, we remap


the first_name property to firstName.

TypeScript
JavaScript
1 class Employee extends Realm.Object {
2 static schema = {
3 name: 'Employee',
4 properties: {
5 _id: 'string',
6 first_name: {type: 'string', mapTo: 'firstName'},
7 },
8 primaryKey: '_id',
9 };
10 }

Define an Asymmetric Object

If you are using Flexible Sync and need to sync a collection unidirectionally
from your decive to your Atlas database, you can set
the asymmetric property on your object schema.

TypeScript
JavaScript
class WeatherSensor extends Realm.Object{
static schema = {
name: 'WeatherSensor',
// sync WeatherSensor objects one way from your device
// to your Atlas database.
asymmetric: true,
primaryKey: '_id',
properties: {
_id: 'objectId',
deviceId: 'string',
temperatureInFahrenheit: 'int',
barometricPressureInHg: 'float',
windSpeedInMph: 'float',
},
};
}

NOTE

Attempting to Read Asymmetric Objects

Asymmetric objects cannot be read. If you attempt to query an asymmetric


object, you will get the following error: "Error: You cannot query an
asymmetric class.".

To learn more about Data Ingest, read Stream Data to Atlas.

TypeScript and Required Properties

We recommend creating Realm objects with Realm.create(), but you can also
use the new operator for your object model's class.

If you use new, you must add your class as a generic, along with any required
properties, when extending Realm.Object. This enables full TypeScript
support for your object model, including type errors when required fields are
not defined.

class Book extends Realm.Object<Book, 'name' | 'store'> {


name!: string;
store!: string;
price?: number;
static schema = {
name: 'Book',
properties: {
name: {type: 'string', indexed: true},
store: 'string',
price: 'int?',
},
};
}

Relationships & Embedded Objects - React Native SDK

Realm is now Atlas Device SDK – Learn More

One-to-One Relationship

A one-to-one relationship means an object is related to at most one other


object of a particular type. To define a one-to-one relationship, specify the
property type as the related Realm object type.

EXAMPLE

In the following example of a one-to-one relationship, a PetOwner may own a


single Pet:

TypeScript
JavaScript
1 class PetOwner extends Realm.Object {
2 static schema = {
3 name: 'PetOwner',
4 properties: {
5 name: 'string',
6 birthdate: 'date',
7 pet: 'Pet?',
8 },
9 };
10 }
1 class Pet extends Realm.Object {
2 static schema = {
3 name: 'Pet',
4 properties: {
5 name: 'string',
6 age: 'int',
7 animalType: 'string?',
8 },
9 };
10 }

One-to-Many Relationship
A one-to-many relationship means an object may be related to multiple
objects. To define a to-many relationship, specify a property where the type is
a list or array of the related Realm object type in its object schema.

EXAMPLE

In the following example of a one-to-many relationship, a User may own


multiple Post objects:

TypeScript
JavaScript
1 class User extends Realm.Object {
2 static schema = {
3 name: 'User',
4 properties: {
5 _id: 'objectId',
6 name: 'string',
7 birthdate: 'date?',
8 posts: 'Post[]',
9 },
10 primaryKey: '_id',
11 };
12 }
1 class Post extends Realm.Object {
2 static schema = {
3 name: 'Post',
4 properties: {
5 _id: 'objectId',
6 title: 'string',
7 },
8 primaryKey: '_id',
9 };
10 }

Inverse Relationship

An inverse relationship connects an object to all other objects that refer to it in


a one-to-one or one-to-many relationship. Whenever you define a relationship,
you also implicity create an inverse relationship.

You can use inverse relationships to "backlink" from one object to another
based on a particular relationship. For example, if you define a relationship
that maps User.posts to a list of Post objects, then you could use an inverse
relationship to look up the User object related to a given Post object in this
way.

Define Inverse Relationship Properties

You can assign an inverse relationship to a property in the object schema


using the linkingObjects property type. This lets you access the inverse
relationship like a normal property.

A linkingObjects property backlinks to a specific relationship. You specify


which relationship to backlink with the object type and property name of the
relationship.

EXAMPLE

In the following example of an inverse relationship, each Post object may


belong to a single User object.

TypeScript
JavaScript
1 class User extends Realm.Object {
2 static schema = {
3 name: 'User',
4 properties: {
5 _id: 'objectId',
6 name: 'string',
7 birthdate: 'date?',
8 posts: 'Post[]',
9 },
10 primaryKey: '_id',
11 };
12 }
1 class Post extends Realm.Object {
2 static schema = {
3 name: 'Post',
4 properties: {
5 _id: 'objectId',
6 title: 'string',
7 user: {
8 type: 'linkingObjects',
9 objectType: 'User',
10 property: 'posts',
11 },
12 },
13 primaryKey: '_id',
14 };
15 }

Find Linking Objects In Code

You can find all objects that link to a given object by calling the
object's Realm.Object.linkingObjects() method. This is useful for when you
want to access all linking objects for a particular relationship without adding a
property to the object schema.

EXAMPLE

A User object may reference multiple Post objects in


the User.posts property. You can use the linkingObjects() method to
find all the User objects that reference a given Post:

TypeScript
JavaScript
1 const PostItem = ({_id}) => {
2 const post = useObject(Post, _id);
3 const user = post?.linkingObjects('User', 'posts')[0];
4
5 if (!post || !user)
6 return <Text>The post or user could not be found</Text>;
7 return (
8 <View>
9 <Text>Post title: {post.title}</Text>
10 <Text>Post created by: {user.name}</Text>
11 </View>
12 );
13 };

Filter a Query Based On Linking Objects

You can reference linking objects in an RQL query by using


the @links.<Type>.<Property> syntax. This is useful for when you want to
use inverse relationships in queries and subscriptions. For more information,
see Backlink Queries.

EXAMPLE

A filter can match a Post object based on properties of the User object that
references it. In the following example, the @links operator references the
relationship defined for User.posts. If a User was born on or after January 1,
2000, then their Post objects are included in the query results.

TypeScript
JavaScript
1 const PostsByYoungUsers = () => {
2 const posts = useQuery(Post);
3 const postsByYoungUsers = useMemo(() => {
4 return posts.filtered(
5 '@links.User.posts.birthdate >= 2000-01-01@00:00:00:0',
6 );
7 }, [posts]);
8
9 if (!posts) return <Text>The post was not found.</Text>;
10 return (
11 <View>
12 <Text>Posts By Young Users</Text>
13 {postsByYoungUsers.map((post) => (
14 <Text key={post._id.toHexString()}>
15 {post.title}
16 </Text>
17 ))}
18 </View>
19 );
20 };

Embedded Objects

An embedded object is a special type of Realm object that models complex


data about a specific object. Embedded objects are similar to relationships,
but they provide additional constraints and map more naturally to the
denormalized MongoDB document model.
Realm enforces unique ownership constraints that treat each embedded object
as nested data inside a single, specific parent object. An embedded object
inherits the lifecycle of its parent object and cannot exist as an independent
Realm object. This means that embedded objects cannot have a primary key
and that Realm automatically deletes embedded objects if their parent object
is deleted.

TIP

Embedded object types are reusable and composable

You can use the same embedded object type in multiple parent object types,
and you can embed objects inside other embedded objects. You can even
recursively reference an embedded object type as an optional property in its
own definition.

NOTE

Realm Uses Cascading Deletes for Embedded Objects

When you delete a Realm object, Realm automatically deletes any embedded
objects referenced by that object. Any objects that your application must
persist after the deletion of their parent object should
use relationships instead.

Realm Object Models

To define an embedded object, set embedded to true. You can reference an


embedded object type from parent object types in the same way as you would
define a relationship:

TypeScript
JavaScript
1 class Address extends Realm.Object {
2 static schema = {
3 name: "Address",
4 embedded: true, // default: false
5 properties: {
6 street: "string?",
7 city: "string?",
8 country: "string?",
9 postalCode: "string?",
10 },
11 };
12 }
1 class Contact extends Realm.Object {
2 static schema = {
3 name: "Contact",
4 primaryKey: "_id",
5 properties: {
6 _id: "objectId",
7 name: "string",
8 // Embed a single object
9 address: "Address",
10 },
11 };
12 }
1 class Business extends Realm.Object {
2 static schema = {
3 name: "Business",
4 primaryKey: "_id",
5 properties: {
6 _id: "objectId",
7 name: "string",
8 // Embed an array of objects
9 addresses: { type: "list", objectType: "Address" },
10 },
11 };
12 }

IMPORTANT

Embedded objects cannot have a primary key.

JSON Schema

Embedded objects map to embedded documents in the parent type's schema.


This behavior differs from regular Realm objects, which map to their own
MongoDB collection.

{
"title": "Contact",
"bsonType": "object",
"required": ["_id"],
"properties": {
"_id": { "bsonType": "objectId" },
"name": { "bsonType": "string" },
"address": {
"title": "Address",
"bsonType": "object",
"properties": {
"street": { "bsonType": "string" },
"city": { "bsonType": "string" },
"country": { "bsonType": "string" },
"postalCode": { "bsonType": "string" }
}
}
}
}
{
"title": "Business",
"bsonType": "object",
"required": ["_id", "name"],
"properties": {
"_id": { "bsonType": "objectId" },
"name": { "bsonType": "string" },
"addresses": {
"bsonType": "array",
"items": {
"title": "Address",
"bsonType": "object",
"properties": {
"street": { "bsonType": "string" },
"city": { "bsonType": "string" },
"country": { "bsonType": "string" },
"postalCode": { "bsonType": "string" }
}
}
}
}
}

Change an Object Model - React Native SDK

Realm is now Atlas Device SDK – Learn More

NOTE

Modify Schema Properties of a Synced Realm


The following page demonstrates how to modify schema properties of a local
realm. Learn how to modify schema properties of a synced realm.

When updating your object schema, you must increment the schema version
and perform a migration.

If your schema update adds optional properties or removes properties, Realm


can perform the migration automatically. You only need to increment
the schemaVersion.

For more complex schema updates, you must also manually specify the
migration logic in a migration function. This might include changes such as:

• Adding required properties that must be populated with default values

• Combining fields

• Renaming a field

• Changing a field's type

• Converting from an object to an embedded object

TIP

Bypass Migration During Development

When developing or debugging your application, you may prefer to delete the
realm instead of migrating it. Use
the BaseConfiguration.deleteRealmIfMigrationNeeded property to delete the
database automatically when a schema mismatch requires a migration.

Never release an app to production with this property set to true.

Schema Version

A schema version identifies the state of a realm schema at some point in time.
Realm tracks the schema version of each realm and uses it to map the objects
in each realm to the correct schema.
Schema versions are ascending integers you can include in the realm
configuration. If a client application does not specify a version number, the
realm defaults to version 0.

IMPORTANT

Increment Versions Monotonically

Migrations must update a realm to a higher schema version. Realm throws an


error if a client application uses a schema version that is lower than the
realm's current version or if the specified schema version is the same as the
realm's current version but includes a different schema.

Migrations

A migration is a function that updates a realm and any objects it contains from
one schema version to a newer version. Migrations allow you to change your
object schemas over time to accommodate new features and refactors.

When you create a Configuration with a schema version greater than the
realm's current version, Realm runs a migration function that you define. The
function has access to the realm's version number and incrementally updates
objects in the realm to conform to the new schema.

Realm automatically migrates certain changes, such as new and deleted


properties, but does not automatically set values for new properties unless the
updated object schema specifies a default value. You can define additional
logic in the migration function to further customize property values.

Add a Property

To add a property to a schema, add the new property to the object's class and
set a schemaVersion of the Configuration object.

EXAMPLE
A realm using schema version 0, the default, has a Person object type with
a firstName and lastName property. You decide to add an age property to
the Person class.

To migrate the realm to conform to the updated Person schema, you set the
realm's schema version in the Configuration to 1. Finally, pass the
configuration object to the createRealmContext() method.

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
_id: 'string',
firstName: 'string',
lastName: 'string',
// add a new property, 'age' to the schema
age: 'int',
},
};
}
const config = {
schema: [Person],
// Increment the 'schemaVersion', since 'age' has been added to the schema.
// The initial schemaVersion is 0.
schemaVersion: 2,
};
// pass the configuration object with the updated 'schemaVersion' to
// createRealmContext()
const {RealmProvider} = createRealmContext(config);

Delete a Property

To delete a property from a schema, remove the property from the object's
class and set a schemaVersion of the Configuration object. Deleting a
property will not impact existing objects.

EXAMPLE
A realm using schema version 0, the default, has a Person object type with
a lastName property. You decide to remove the property from the schema.

To migrate the realm to conform to the updated Person schema, set the
realm's schema version to 1 in the Configuration object. Finally, pass the
configuration object to the createRealmContext() method.

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
_id: 'string',
firstName: 'string',
age: 'int',
},
};
}
const config = {
schema: [Person],
// Increment the 'schemaVersion', since 'lastName' has been removed from the schema.
// The initial schemaVersion is 0.
schemaVersion: 1,
};
// pass the configuration object with the updated 'schemaVersion' to createRealmContext()
const {RealmProvider} = createRealmContext(config);

Rename a Property

To rename an object property, change the property name in the object schema
and then create a realm configuration with an incremented schema
version and a migration function that updates existing objects to use the new
property name.

Migrations do not allow you to directly rename a property. Instead, you can
create a new property with the updated name, copy the value from the old
property, and then delete the old property.

EXAMPLE
A realm using schema version 0, the default, has a Person object type. The
original schema had a firstName and lastName field. You later decide that
the Person class should use a combined fullName field and removes the
separate firstName and lastName fields.

To migrate the realm to conform to the updated Person schema, create


a Configuration object and set the realm's schema version to 1, and define a
migration function that sets the value of fullName based on the
existing firstName and lastName properties. Finally, pass the configuration
object to the createRealmContext() method.

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
_id: 'string',
// rename the 'firstName' and 'lastName' property, to 'fullName'
// in the schema
fullName: 'string',
age: 'int',
},
};
}
const config = {
schema: [Person],
// Increment the 'schemaVersion', since 'fullName' has replaced
// 'firstName' and 'lastName' in the schema.
// The initial schemaVersion is 0.
schemaVersion: 1,
onMigration: (oldRealm, newRealm) => {
// only apply this change if upgrading schemaVersion
if (oldRealm.schemaVersion < 1) {
const oldObjects =
oldRealm.objects(Person);
const newObjects = newRealm.objects(Person);
// loop through all objects and set the fullName property in the
// new schema
for (const objectIndex in oldObjects) {
const oldObject = oldObjects[objectIndex];
const newObject = newObjects[objectIndex];
newObject.fullName = `${oldObject.firstName} ${oldObject.lastName}`;
}
}
},
};
// pass the configuration object with the updated 'schemaVersion' and
// 'migration' function to createRealmContext()
const {RealmProvider} = createRealmContext(config);

IMPORTANT

Synced Realms

Synced realms only support non-breaking - also called additive - changes to


ensure that older clients can sync with newer clients. Because full renames
require you to delete the old property, you cannot rename a synchronized
property without requiring a client reset. Instead, consider adding the renamed
property without deleting the old property. Alternately, use mapTo to store
data using the existing internal name, but let your code use a different name.

Modify a Property Type

To modify a property's type, set the property type of the field that you wish to
modify to the new data type. Then, set a schemaVersion, and
a migration callback function of the Configuration object.

NOTE

Synchronized realms only support non-breaking changes to ensure that older


clients can sync with newer clients. This means that synchronized realms do
not support modifying the type of a property of a schema.

EXAMPLE

A realm using schema version 0, the default, has a Person object type. The
original schema had an _id with a property type of int. You later decide that
the Person class's _id field should be of type ObjectId, and updates the
schema.

To migrate the realm to conform to the updated Person schema, create


a Configuration object and set the realm's schema version to 1, and define
a migration function to convert the integer type to an Object ID type. Finally,
pass the configuration object to the createRealmContext() method.

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
// update the data type of '_id' to be 'objectId' within the schema
_id: 'objectId',
firstName: 'string',
lastName: 'string',
},
};
}
const config = {
schema: [Person],
// Increment the 'schemaVersion', since the property type of '_id'
// has been modified.
// The initial schemaVersion is 0.
schemaVersion: 1,
onMigration: (oldRealm, newRealm) => {
if (oldRealm.schemaVersion < 1) {
const oldObjects =
oldRealm.objects(Person);
const newObjects = newRealm.objects(Person);
// loop through all objects and set the _id property
// in the new schema
for (const objectIndex in oldObjects) {
const oldObject = oldObjects[objectIndex];
const newObject = newObjects[objectIndex];
newObject._id = new Realm.BSON.ObjectId(oldObject._id);
}
}
},
};
// Pass the configuration object with the updated
// 'schemaVersion' and 'migration' function to createRealmContext()
const {RealmProvider} = createRealmContext(config);

Change an Object Model - React Native SDK


Realm is now Atlas Device SDK – Learn More

NOTE

Modify Schema Properties of a Synced Realm

The following page demonstrates how to modify schema properties of a local


realm. Learn how to modify schema properties of a synced realm.

When updating your object schema, you must increment the schema version
and perform a migration.

If your schema update adds optional properties or removes properties, Realm


can perform the migration automatically. You only need to increment
the schemaVersion.

For more complex schema updates, you must also manually specify the
migration logic in a migration function. This might include changes such as:

• Adding required properties that must be populated with default values

• Combining fields

• Renaming a field

• Changing a field's type

• Converting from an object to an embedded object

TIP

Bypass Migration During Development

When developing or debugging your application, you may prefer to delete the
realm instead of migrating it. Use
the BaseConfiguration.deleteRealmIfMigrationNeeded property to delete the
database automatically when a schema mismatch requires a migration.

Never release an app to production with this property set to true.


Schema Version

A schema version identifies the state of a realm schema at some point in time.
Realm tracks the schema version of each realm and uses it to map the objects
in each realm to the correct schema.

Schema versions are ascending integers you can include in the realm
configuration. If a client application does not specify a version number, the
realm defaults to version 0.

IMPORTANT

Increment Versions Monotonically

Migrations must update a realm to a higher schema version. Realm throws an


error if a client application uses a schema version that is lower than the
realm's current version or if the specified schema version is the same as the
realm's current version but includes a different schema.

Migrations

A migration is a function that updates a realm and any objects it contains from
one schema version to a newer version. Migrations allow you to change your
object schemas over time to accommodate new features and refactors.

When you create a Configuration with a schema version greater than the
realm's current version, Realm runs a migration function that you define. The
function has access to the realm's version number and incrementally updates
objects in the realm to conform to the new schema.

Realm automatically migrates certain changes, such as new and deleted


properties, but does not automatically set values for new properties unless the
updated object schema specifies a default value. You can define additional
logic in the migration function to further customize property values.
Add a Property

To add a property to a schema, add the new property to the object's class and
set a schemaVersion of the Configuration object.

EXAMPLE

A realm using schema version 0, the default, has a Person object type with
a firstName and lastName property. You decide to add an age property to
the Person class.

To migrate the realm to conform to the updated Person schema, you set the
realm's schema version in the Configuration to 1. Finally, pass the
configuration object to the createRealmContext() method.

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
_id: 'string',
firstName: 'string',
lastName: 'string',
// add a new property, 'age' to the schema
age: 'int',
},
};
}
const config = {
schema: [Person],
// Increment the 'schemaVersion', since 'age' has been added to the schema.
// The initial schemaVersion is 0.
schemaVersion: 2,
};
// pass the configuration object with the updated 'schemaVersion' to
// createRealmContext()
const {RealmProvider} = createRealmContext(config);

Delete a Property
To delete a property from a schema, remove the property from the object's
class and set a schemaVersion of the Configuration object. Deleting a
property will not impact existing objects.

EXAMPLE

A realm using schema version 0, the default, has a Person object type with
a lastName property. You decide to remove the property from the schema.

To migrate the realm to conform to the updated Person schema, set the
realm's schema version to 1 in the Configuration object. Finally, pass the
configuration object to the createRealmContext() method.

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
_id: 'string',
firstName: 'string',
age: 'int',
},
};
}
const config = {
schema: [Person],
// Increment the 'schemaVersion', since 'lastName' has been removed from the schema.
// The initial schemaVersion is 0.
schemaVersion: 1,
};
// pass the configuration object with the updated 'schemaVersion' to createRealmContext()
const {RealmProvider} = createRealmContext(config);

Rename a Property

To rename an object property, change the property name in the object schema
and then create a realm configuration with an incremented schema
version and a migration function that updates existing objects to use the new
property name.
Migrations do not allow you to directly rename a property. Instead, you can
create a new property with the updated name, copy the value from the old
property, and then delete the old property.

EXAMPLE

A realm using schema version 0, the default, has a Person object type. The
original schema had a firstName and lastName field. You later decide that
the Person class should use a combined fullName field and removes the
separate firstName and lastName fields.

To migrate the realm to conform to the updated Person schema, create


a Configuration object and set the realm's schema version to 1, and define a
migration function that sets the value of fullName based on the
existing firstName and lastName properties. Finally, pass the configuration
object to the createRealmContext() method.

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
_id: 'string',
// rename the 'firstName' and 'lastName' property, to 'fullName'
// in the schema
fullName: 'string',
age: 'int',
},
};
}
const config = {
schema: [Person],
// Increment the 'schemaVersion', since 'fullName' has replaced
// 'firstName' and 'lastName' in the schema.
// The initial schemaVersion is 0.
schemaVersion: 1,
onMigration: (oldRealm, newRealm) => {
// only apply this change if upgrading schemaVersion
if (oldRealm.schemaVersion < 1) {
const oldObjects =
oldRealm.objects(Person);
const newObjects = newRealm.objects(Person);
// loop through all objects and set the fullName property in the
// new schema
for (const objectIndex in oldObjects) {
const oldObject = oldObjects[objectIndex];
const newObject = newObjects[objectIndex];
newObject.fullName = `${oldObject.firstName} ${oldObject.lastName}`;
}
}
},
};
// pass the configuration object with the updated 'schemaVersion' and
// 'migration' function to createRealmContext()
const {RealmProvider} = createRealmContext(config);

IMPORTANT

Synced Realms

Synced realms only support non-breaking - also called additive - changes to


ensure that older clients can sync with newer clients. Because full renames
require you to delete the old property, you cannot rename a synchronized
property without requiring a client reset. Instead, consider adding the renamed
property without deleting the old property. Alternately, use mapTo to store
data using the existing internal name, but let your code use a different name.

Modify a Property Type

To modify a property's type, set the property type of the field that you wish to
modify to the new data type. Then, set a schemaVersion, and
a migration callback function of the Configuration object.

NOTE

Synchronized realms only support non-breaking changes to ensure that older


clients can sync with newer clients. This means that synchronized realms do
not support modifying the type of a property of a schema.

EXAMPLE
A realm using schema version 0, the default, has a Person object type. The
original schema had an _id with a property type of int. You later decide that
the Person class's _id field should be of type ObjectId, and updates the
schema.

To migrate the realm to conform to the updated Person schema, create


a Configuration object and set the realm's schema version to 1, and define
a migration function to convert the integer type to an Object ID type. Finally,
pass the configuration object to the createRealmContext() method.

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
// update the data type of '_id' to be 'objectId' within the schema
_id: 'objectId',
firstName: 'string',
lastName: 'string',
},
};
}
const config = {
schema: [Person],
// Increment the 'schemaVersion', since the property type of '_id'
// has been modified.
// The initial schemaVersion is 0.
schemaVersion: 1,
onMigration: (oldRealm, newRealm) => {
if (oldRealm.schemaVersion < 1) {
const oldObjects =
oldRealm.objects(Person);
const newObjects = newRealm.objects(Person);
// loop through all objects and set the _id property
// in the new schema
for (const objectIndex in oldObjects) {
const oldObject = oldObjects[objectIndex];
const newObject = newObjects[objectIndex];
newObject._id = new Realm.BSON.ObjectId(oldObject._id);
}
}
},
};
// Pass the configuration object with the updated
// 'schemaVersion' and 'migration' function to createRealmContext()
const {RealmProvider} = createRealmContext(config);

Property Types - React Native SDK

Realm is now Atlas Device SDK – Learn More

Realm supports the following property data types:

• bool maps to the JavaScript Boolean type

• int maps to the JavaScript Number type. Internally, Realm


stores int with 64 bits.

• float maps to the JavaScript Number type. Internally, Realm


stores float with 32 bits.

• double maps to the JavaScript Number type. Internally, Realm


stores double with 64 bits.

• string maps to the JavaScript String type.

• decimal128 for high precision numbers.

• objectId maps to BSON ObjectId type.

• data maps to the JavaScript ArrayBuffer type.

• date maps to the JavaScript Date type.

• list maps to the JavaScript Array type. You can also specify that a
field contains a list of primitive value type by appending [] to the type
name.

• linkingObjects is a special type used to define an inverse


relationship.

• dictionary used to manage a collection of unique String keys paired


with values. The Dictionary data type is available in the realm-
[email protected] release.
• set is based on the JavaScript Set type. Realm Set is available in
the [email protected] release.

• mixed is a property type that can hold different data types.


The Mixed data type is available in the [email protected] release.

• uuid is a universally unique identifier from Realm.BSON. The UUID data


type is available in the [email protected] release.

To learn how specific data types are mapped to BSON types in an App
Services Schema, refer to Data Model Mapping in the Atlas App Services
documentation.

Collections - React Native SDK

Realm is now Atlas Device SDK – Learn More

Realm has several types to represent groups of objects, which we


call collections. A collection is an object that contains zero or more instances
of one Realm type.

You can filter and sort any collection using Realm's query engine. Collections
are live, so they always reflect the current state of the realm instance on the
current thread. You can also listen for changes in the collection by
subscribing to collection notifications.

Results

A Results collection represents the lazily-evaluated results of a query


operation. Results are immutable: you cannot add or remove elements to or
from the results collection. Results have an associated query that determines
their contents.

TIP
See also:

Reads

Lists

A List represents a to-many relationship between two Realm types. Lists are
mutable: within a write transaction, you can add and remove elements to and
from a list. Lists are not associated with a query and are declared as a
property of an object model.

TIP

See also:

To-Many Relationships

Results are Lazily Evaluated

Realm only runs a query when you request the results of that query. This lazy
evaluation enables you to write elegant, highly-performant code for handling
large data sets and complex queries.

Collections are Live

Like live objects, Realm collections are usually live:

• Live results collections always reflect the current results of the


associated query.

• Live lists always reflect the current state of the relationship on the realm
instance.

A collection is not live when:

• it is a results collection that you are iterating through using


a for..in or for..of statement. Both statements will continue to iterate
through objects in the collection even if you have deleted or modified
the collection's objects to exclude them from the filter that produced the
results collection.

• the collection is a frozen Results.snapshot().

Combined with collection notifications, live collections enable reactive code.


For example, suppose your view displays the results of a query. You can keep
a reference to the results collection in your view class, then read the results
collection as needed without having to refresh it or validate that it is up-to-
date.

IMPORTANT

Indexes may change

Since results update themselves automatically, do not store the positional


index of an object in the collection or the count of objects in a collection. The
stored index or count value could be outdated by the time you use it.

Working With Collections

Limiting Query Results

As a result of lazy evaluation, you do not need any special mechanism to limit
query results with Realm. For example, if your query matches thousands of
objects, but you only want to load the first ten, access only the first ten
elements of the results collection.

Pagination

Thanks to lazy evaluation, the common task of pagination becomes quite


simple. For example, suppose you have a results collection associated with a
query that matches thousands of objects in your realm. You display one
hundred objects per page. To advance to any page, simply access the
elements of the results collection starting at the index that corresponds to the
target page.

Summary

• A Realm collection is a homogenous container of zero or more


instances of one Realm type.

• There are two main kinds of collection: lists and results. Lists define
the to-many relationships of your Realm types, while results represent
the lazily-loaded output of a read operation.

• Lazy evaluation of results collections means there is no need to design


a special query to get limited or paginated results. Perform the query
and read from the results collection as needed.

• Data in Realm is live, which means that an object always reflects its
most recent saved state.

Dictionaries - React Native SDK

Realm is now Atlas Device SDK – Learn More

New in version [email protected] .

You can use the Realm.Dictionary data type to manage a collection of unique
String keys paired with values. The dictionary data maps to the
Javascript Object type.

For example, creating a HomeOwner Realm object where the home property is
defined as a dictionary type could look like this:

realm.create('HomeOwner', {
name: 'Anna Smith',
home: {address: '2 jefferson lane', yearRenovated: 1994, color: 'blue'},
});
Realm Object Models

You can define define a dictionary of mixed values for a Realm object model in
three ways:

• set the data type of your field to an empty object, "{}".

• Add the data type before the brackets to create a dictionary with values
of a specific type. For example, "int{}" to specify that dictionary
values must be integers or "string{}" to specify that dictionary values
must be strings.

• Define the object type explicitly. This is necessary for using object
Types in your Realm as ditionary values.
TypeScript
JavaScript
class HomeOwner extends Realm.Object {
static schema = {
name: 'HomeOwner',
properties: {
name: 'string',
home: '{}',
pets: {
type: 'dictionary',
objectType: 'Pet',
optional: true
},
},
};
}

Realm disallows the use of . or $ characters in map keys. You can use
percent encoding and decoding to store a map key that contains one of these
disallowed characters.

// Percent encode . or $ characters to use them in map keys


const mapKey = "kitchen.windows";
const encodedMapKey = mapKey.replace(".", "%2E");

Create an Object with a Dictionary Value


In the following CreateHomeOwner example, we create a new object with a
dictionary property.

The CreateHomeOwner component does the following:

• Create React state


that represents the homeowner's name and address, respectively.
• Get access to an open realm instance by calling the useRealm() hook within
the component.
• Create a component method SubmitHomeOwner() that performs a write
transaction and creates a new HomeOwner object based on
the TextInput values for the homeowner's name and address, respectively.
• Add an onPress event on
the submit button that calls SubmitHomeOwner()
1 const CreateHomeOwner = () => {
2 const [homeOwnerName, setHomeOwnerName] = useState('John Smith');
3 const [address, setAddress] = useState('1 Home Street');
4 const realm = useRealm();
5
6 const submitHomeOwner = () => {
7 // Create a HomeOwner realm object within a Write Transaction
8 realm.write(() => {
9 realm.create('HomeOwner', {
10 name: homeOwnerName,
11 // For the dictionary field, 'home', set the value
12 // to a regular JavaScript object
13 home: {
14 address,
15 },
16 });
17 });
18 };
19 return (
20 <View>
21 <TextInput
22 value={homeOwnerName}
23 onChangeText={text => setHomeOwnerName(text)}
24 />
25 <TextInput value={address} onChangeText={text => setAddress(text)} />
26 <Button
27 title='Submit Home Owner'
28 onPress={submitHomeOwner}
29 />
30 </View>
31 );
32 };

Query for Objects with a Dictionary Property

To filter a query, run collection.filtered() to specify a subset of results based


on the value(s) of one or more object properties. You can specify results
based on the value of a dictionary's properties by using bracket notation.

You can also determine whether a results collection has a certain key or value
by using <dictionary>.@keys or <dictionary>.@values. For instance, if
you had a HomeOwner collection with a nested home dictionary, you could
return all HomeOwner objects with a home with a "price" property by running
the query: home.@keys = "price".

Example

In the following HomeList example, we query for objects that have dictionary
properties.

The HomeList component does the following:

• Performs a query for all homeowners by passing the HomeOwner class


to the useQuery hook.

• Performs a query for homeowners with a listed price by


passing collection.filtered() the query: home.@keys = "price".

• Performs a query for the summer hill house by


running collection.filtered() using bracket notation to find the
first homeowner with an address set to "Summerhill St." and getting
their home by using dot syntax.

• Performs a query for all homeowners with any field with a value of red
by passing collection.filtered() the query: 'home.@values =
"red"'. We then get the first homeowner's home.
• Display the results of our queries in the UI by rendering information
about the homes
TypeScript
JavaScript
1 const HomeList = () => {
2 // query for all HomeOwner objects
3 const homeOwners = useQuery(HomeOwner);
4
5 // run the `.filtered()` method on all the returned homeOwners to
6 // find all homeOwners that have a house with a listed price
7 const listedPriceHomes = homeOwners.filtered('home.@keys = "price"');
8
9 // run the `.filtered()` method on all the returned homeOwners to
10 // find the house with the address "Summerhill St."
11 const summerHillHouse = homeOwners.filtered(
12 'home["address"] = "Summerhill St."',
13 )[0].home;
14
15 // run the `.filtered()` method on all the returned homeOwners to
16 // find the first house that has any field with a value of 'red'
17 const redHouse = homeOwners.filtered('home.@values = "red"')[0].home;
18 return (
19 <View>
20 <Text>All homes:</Text>
21 {homeOwners.map(homeOwner => (
22 <View>
23 <Text>{homeOwner.home.address}</Text>
24 </View>
25 ))}
26
27 <Text>All homes with a price:</Text>
28 {listedPriceHomes.map(homeOwner => (
29 <View>
30 <Text>{homeOwner.home.address}</Text>
31 <Text>{homeOwner.home.price}</Text>
32 </View>
33 ))}
34
35 <Text>Summer Hill House:</Text>
36 <Text>{summerHillHouse.address}</Text>
37 <Text>{summerHillHouse.color}</Text>
38
39 <Text>Red House:</Text>
40 <Text>{redHouse.address}</Text>
41 </View>
42 );
43 };
Update a Dictionary

Update a dictionary's property by using the dictionary.set() method or dot


notation to set its property to a new value.

Example

In the following UpdateHome example, we update a dictionary's property.

The UpdateHome component does the following:

• Create a React state


variable that represents the home address.
• Get access to an opened realm instance by calling the useRealm() hook
within the component.
• Create a component method updateAddress() that performs a write
transaction and uses dictionary.set() to set the home's address to the
value of the address state variable. It also uses dot syntax to set
the yearRenovated to 2004.

• Render a TextInput that displays and changes the address state


variable.
• Add an onPress event on
the "Update Address" button that calls updateAddress()
TypeScript
JavaScript
1 const UpdateHome = ({homeOwnerName}) => {
2 const [address, setAddress] = useState('');
3 const realm = useRealm();
4 const homeOwner = useQuery(HomeOwner).filtered(
5 `name == '${homeOwnerName}'`,
6 )[0];
7
8 const updateAddress = () => {
9 // Update the home object with the new address
10 realm.write(() => {
11 // use the `set()` method to update a field of a dictionary
12 homeOwner.home.set({address});
13 // alternatively, update a field of a dictionary through dot notation
14 homeOwner.home.yearRenovated = 2004;
15 });
16 };
17
18 return (
19 <View>
20 <Text>{homeOwner.name}</Text>
21 <TextInput
22 value={address}
23 onChangeText={setAddress}
24 placeholder='Enter new address'
25 />
26 <Button
27 onPress={updateAddress}
28 title='Update Address'
29
30 />
31 </View>
32 );
33 };

Delete Members of a Dictionary

To delete members of a dictionary, use the dictionary.remove() method with an


array of properties to remove from the dictionary.

Example

In the following HomeInfo example, we delete members of a dictionary.

The HomeInfo component does the following:

• Get access to an open realm instance by calling the useRealm() hook


within the component.

• Retrieve the first homeowner that matches the name passed into the
component as a prop. We do this by getting the first value returned from
the query: useQuery(HomeOwner).filtered(`name ==
'${homeOwnerName}'`).

• Create a component method deleteExtraHomeInfo() that performs a


write transaction and calls dictionary.remove() to remove
the yearRenovated and color properties.
• Render the homeowner's name and home address in the UI.

• Add an onPress event on the "Delete extra home info" button that
calls deleteExtraHomeInfo().
TypeScript
JavaScript
1 const HomeInfo = ({homeOwnerName}) => {
2 const realm = useRealm();
3 const homeOwner = useQuery(HomeOwner).filtered(
4 `name == '${homeOwnerName}'`,
5 )[0];
6
7 const deleteExtraHomeInfo = () => {
8 realm.write(() => {
9 // remove the 'yearRenovated' and 'color' field of the house
10 homeOwner.home.remove(['yearRenovated', 'color']);
11 });
12 };
13
14 return (
15 <View>
16 <Text>{homeOwner.name}</Text>
17 <Text>{homeOwner.home.address}</Text>
18 <Button
19 onPress={deleteExtraHomeInfo}
20 title='Delete extra home info'
21
22 />
23 </View>
24 );
25 };

Sets - React Native SDK

Realm is now Atlas Device SDK – Learn More

New in version [email protected] .

A Realm Set is a special object that allows you to store a collection of unique
values. Realm Sets are based on JavaScript sets, but can only contain values
of a single type and can only be modified within a write transaction. Sets allow
you to perform math operations such as finding the union, intersection, or
difference between two Sets. To learn more about performing these
operations, see the MDN docs for Implementing basic set operations.

Realm Object Models

You can define a Realm object model property type as a Realm Set, in a two
ways:

• Specify the data type the Set will contain, followed by <>.

• Use object notation and the type field for more complicated properties.
TypeScript
JavaScript
1 class Character extends Realm.Object {
2 static schema = {
3 name: 'Character',
4 primaryKey: '_id',
5 properties: {
6 _id: 'objectId',
7 name: 'string',
8 levelsCompleted: 'int<>',
9 inventory: {
10 type: 'set',
11 objectType: 'string',
12 }
13 },
14 };
15 }

Create an Object With a Set

To create an object with a Realm Set property, you must create the object
within a write transaction. When defining your Realm object, initialize the
Realm Set by passing an empty array or an array with your initial values.

Example

In the following example of a CreateInitialCharacters component, we


create Character objects with Set properties.
The CreateInitialCharacters component does the following:

• Gets access to an opened realm instance by calling


the useRealm() hook within the component.

• Uses React's useEffect hook to call an anonymous function only once


with useEffect and an empty dependency array. Within the
anonymous function, we create two different Character objects within
a write transaction. We set each
character's inventory and levelsCompleted sets as an array with
initial values.

• Retrieves all characters in the realm instance by passing


the Character class to the useQuery() hook.

• Displays each character's name in the UI as a Text element.


TypeScript
JavaScript
1 const CreateInitialCharacters = () => {
2 const realm = useRealm();
3 useEffect(() => {
4 realm.write(() => {
5 realm.create('Character', {
6 _id: new Realm.BSON.ObjectId(),
7 name: 'AdventurousPlayer',
8 inventory: ['elixir', 'compass', 'glowing shield'],
9 levelsCompleted: [4, 9],
10 });
11 });
12 realm.write(() => {
13 realm.create('Character', {
14 _id: new Realm.BSON.ObjectId(),
15 name: 'HealerPlayer',
16 inventory: ['estus flask', 'gloves', 'rune'],
17 levelsCompleted: [1, 2, 5, 24],
18 });
19 });
20 }, []);
21 const characters = useQuery(Character);
22
23 return (
24 <View>
25 {characters.map(character => (
26 <View key={character._id}>
27 <Text>{character.name}</Text>
28 </View>
29 ))}
30 </View>
31 );
32 };

Add Items to a Set

To add an item to a Set, pass the new value to the Realm.Set.add() method
method within a write transaction.

Example

In the following example of a AddInventoryToCharacter component, we add


new Set elements to the character's inventory.

The AddInventoryToCharacter component does the following:

• Gets access to an opened realm instance by calling


the useRealm() hook within the component.

• Creates a state variable called "inventoryItem" that represents the new


inventory item to add to the inventory Set.

• Retrieves the character by passing the Character class to


the useQuery() hook and running the Collection.filtered() method on
the result to filter for characters with the name matching
the characterName prop. Then we set the variable character to the
first matching result.

• Creates a component method addInventoryItem() that performs a


write transaction that adds an inventory item to the character's
inventory by passing the inventoryItem state variable
to Realm.Set.add().

• Renders a TextInput that changes the inventoryItem state variable,


and a Button that calls the addInventoryItem() method.
TypeScript
JavaScript
1 const AddInventoryToCharacter = ({characterName}) => {
2 const realm = useRealm();
3 const [inventoryItem, setInventoryItem] = useState('');
4 const character = useQuery(Character).filtered(
5 `name = '${characterName}'`,
6 )[0];
7
8 const addInventoryItem = () => {
9 realm.write(() => {
10 character?.inventory.add(inventoryItem);
11 });
12 };
13
14 return (
15 <View>
16 <TextInput
17 onChangeText={text => setInventoryItem(text)}
18 value={inventoryItem}
19 />
20 <Button
21 title='Add Inventory Item'
22 onPress={addInventoryItem}
23 />
24 </View>
25 );
26 };

Check if a Set has Specific Items and Check the Size of a Set

You may want to check for information about your Set, such as its size or if it
contains specific item.

To determine if a Set contains a particular value, pass the value to


the Realm.Set.has() method. This method will return true if the Set contains
the value specified.

To discover how many items are in a Set, you can check its size property.

Example

In the following example of a QueryCharacterInventory component, we


check the character's inventory size and if it has a specific item.
The QueryCharacterInventory component does the following:

• Creates a state variable called "inventoryItem" that represents the


inventory item that you want to search the character's inventory for.

• Uses the useQuery hook to perform a query for all characters, and filter
the results to only include the characters with the name matching
the characterName passed to the component as a prop. Then we get
the first matching result.

• Retrieves the character by passing the Character class to


the useQuery() hook and running the Collection.filtered() method on
the result to filter for characters with the name matching
the characterName prop. Then we set the variable character to the
first matching result.

• Creates a component method queryCharacterInventory that passes


the inventoryItem state variable to Realm.Set.has() to check if the
character's inventory contains the item. If the character's inventory
contains the item, the method alerts that the character has the item. If
the character's inventory does not contain the item, the method alerts
that the character does not have the item.

• Renders the character's name, and renders the inventory size using
the size property of the character's inventory. It also renders
a TextInput that changes the inventoryItem state variable, and
a Button that calls the queryCharacterInventory method.
TypeScript
JavaScript
1 const QueryCharacterInventory = ({characterName}) => {
2 const [inventoryItem, setInventoryItem] = useState('');
3 const character = useQuery(Character).filtered(
4 `name = '${characterName}'`,
5 )[0];
6
7 const queryCharacterInventory = () => {
8 const characterDoesHaveItem = character.inventory.has(inventoryItem);
9 if (characterDoesHaveItem) {
10 Alert.alert(`Character has item: ${inventoryItem}`);
11 } else {
12 Alert.alert(`Item not found in character's inventory`);
13 }
14 };
15 return (
16 <View>
17 <Text>{character.name}</Text>
18 <Text>
19 Total number of inventory items: {character.inventory.size}
20 </Text>
21 <TextInput
22 onChangeText={text => setInventoryItem(text)}
23 value={inventoryItem}
24 />
25 <Button
26 title='Query for Inventory'
27 onPress={queryCharacterInventory}
28 />
29 </View>
30 );
31 };

Remove Set Information

You may want to remove a specific item or all items from a Set.

To remove a specific value from a Set, pass the value to


the Realm.Set.delete() method within a write transaction.

To clear the Set, run the Realm.Set.clear() method within a write transaction.

Example

In the following example of a RemoveInventoryFromCharacter component,


we remove a specific item from the Set and clear the Set of all items.

The RemoveInventoryFromCharacter component does the following:

• Gets access to an opened realm instance by calling


the useRealm() hook within the component.

• Creates a state variable called "inventoryItem" that represents the


inventory item to remove from the inventory Set.
• Creates a component method removeInventoryItem that passes
the inventoryItem state variable to Realm.Set.delete() to remove
the item from the character's inventory.

• Creates a component method removeAllInventory that


calls Realm.Set.clear() to remove all items from the character's
inventory.

• Renders a TextInput that changes the inventoryItem state variable,


and two Button components that call
the removeInventoryItem and removeAllInventory methods,
respectively.
TypeScript
JavaScript
1 const RemoveInventoryFromCharacter = ({characterName}) => {
2 const realm = useRealm();
3 const [inventoryItem, setInventoryItem] = useState('');
4 const character = useQuery(Character).filtered(
5 `name = '${characterName}'`,
6 )[0];
7
8 const removeInventoryItem = () => {
9 realm.write(() => {
10 character?.inventory.delete(inventoryItem);
11 });
12 };
13 const removeAllInventory = () => {
14 realm.write(() => {
15 character?.inventory.clear();
16 });
17 };
18 return (
19 <View>
20 <Text>{character.name}</Text>
21 <TextInput
22 onChangeText={text => setInventoryItem(text)}
23 value={inventoryItem}
24 />
25 <Button
26 title='Remove Inventory Item'
27 onPress={removeInventoryItem}
28 />
29 <Button
30 title='Remove All Inventory'
31 onPress={removeAllInventory}
32 />
33 </View>
34 );
35 };

Traverse a Set

You can traverse a Set to access each item in the Set. To traverse a Set, use
the Set.map() method or alternative iteration method.

However, by default the order of the items in a Set is not guaranteed. To


traverse a Set in order, you can store the Set's items in a state variable and
update that state variable when you add new items to the Set.

Example

In the following example of a TraverseCharacterInventory component, a


character starts with no inventory items. When the user adds items to the
inventory Set, the component displays each item in the Set in both an ordered
and unordered list.

The TraverseCharacterInventory component does the following:

• Gets access to an opened realm instance by calling


the useRealm() hook within the component.

• Creates a state variable called "inventoryItem" that represents the new


inventory item to add to the inventory Set.

• Creates a state variable called "inventory" that will hold the character's
inventory items in order of insertion.

• Retrieves the character by passing the Character class to


the useQuery() hook and running the Collection.filtered() method on
the result to filter for characters with the name matching
the characterName prop. Then we set the variable character to the
first matching result.
• Creates a component method addInventoryItem() that performs a
write transaction that adds an inventory item to the character's
inventory by passing the inventoryItem state variable to
the Realm.Set.add() method. After the write transaction, the method
adds the inventoryItem to the inventory array state variable.

• Renders a TextInput that changes the inventoryItem state variable,


and a Button that calls the addInventoryItem() method.

• Renders a list of the character's inventory items in the order they were
added to the Set by iterating through the inventory array state
variable.

• Renders a unordered list of the character's inventory by iterating


through character.inventory.
TypeScript
JavaScript
1 const TraverseCharacterInventory = ({characterName}) => {
2 const realm = useRealm();
3 const [inventoryItem, setInventoryItem] = useState('');
4 const [inventory, setInventory] = useState([]);
5
6 const character = useQuery(Character).filtered(
7 `name = '${characterName}'`,
8 )[0];
9
10 const addInventoryItem = () => {
11 realm.write(() => {
12 character?.inventory.add(inventoryItem);
13 });
14 setInventory([...inventory, inventoryItem]);
15 };
16
17 return (
18 <View>
19 <Text>{character.name}</Text>
20 <Text>Add an item to the inventory:</Text>
21 <TextInput
22 onChangeText={text => setInventoryItem(text)}
23 value={inventoryItem}
24 />
25 <Button
26 title='Add Inventory Item'
27 onPress={addInventoryItem}
28 />
29
30 <Text>Ordered Inventory:</Text>
31 {inventory.map(item => (
32 <Text>{item}</Text>
33 ))}
34
35 <Text>Unordered Inventory:</Text>
36 {character.inventory.map(item => (
37 <Text>{item}</Text>
38 ))}
39 </View>
40 );
41 };

Mixed - React Native SDK

Realm is now Atlas Device SDK – Learn More

New in version [email protected] .

The Mixed data type is a realm property type that can hold any valid Realm
data type except a collection. You can create collections (lists, sets, and
dictionaries) of type mixed, but a mixed type itself cannot be a collection.

The Mixed type is indexable, but you can't use it as a primary key.

Properties using the Mixed type can hold null values and cannot be defined as
optional. All instances of the JavaScript Number type in a Realm Mixed type
are mapped to the Realm double type.

Realm Object Models

To set a property of your object model as Mixed, set the property's type
to mixed.

TypeScript
JavaScript
class Cat extends Realm.Object {
static schema = {
name: 'Cat',
properties: {
name: 'string',
birthDate: 'mixed',
},
};
}

Create an Object With a Mixed Value

Create an object with a Mixed value by using the new operator within a write
transaction.

Example

In the following CreateCatsInput example, we create several Cat realm


objects with a Mixed type for the birthDate field.

The CreateCatsInput component does the following:

• Get access to the opened realm instance by calling


the useRealm() hook.
• Use React's useEffect hook
to call an anonymous function only once with useEffect and an empty
dependency array.
• Within the anonymous function, we create four different Cat objects by
using the new operator to create a new realm object within a write transaction.
Each of the Cat objects uses a different data type for the birthDate property.

• Use the useQuery() hook to retrieve all Cat objects.


• Map through the cats to render
a list of Text components displaying each cat's name and birthDate.
TypeScript
JavaScript
1 const CreateCatsInput = () => {
2 const realm = useRealm();
3
4 useEffect(() => {
5 // Add data to the Realm when the component mounts
6 realm.write(() => {
7 // create a Cat with a birthDate value of type string
8 realm.create('Cat', {
9 name: 'Euler',
10 birthDate: 'December 25th, 2017',
11 });
12
13 // create a Cat with a birthDate value of type date
14 realm.create('Cat', {
15 name: 'Blaise',
16 birthDate: new Date('August 17, 2020'),
17 });
18
19 // create a Cat with a birthDate value of type int
20 realm.create('Cat', {name: 'Euclid', birthDate: 10152021});
21
22 // create a Cat with a birthDate value of type null
23 realm.create('Cat', {name: 'Pythagoras', birthDate: null});
24 });
25 }, []);
26
27 // retrieve all cats
28 const cats = useQuery(Cat);
29
30 return (
31 <>
32 {cats.map(cat => (
33 <View>
34 <Text>{cat.name}</Text>
35 <Text>{String(cat.birthDate)}</Text>
36 </View>
37 ))}
38 </>
39 );
40 };

Query for Objects with a Mixed Value

To query for objects with a Mixed value, run the Collection.filtered() method
and pass in a filter for a non-Mixed field. You can then print the value of the
Mixed property or the entire object itself.

Example

In the following CatInfoCard example, we query for a Cat object using the
cat's name.
The CatInfoCard component does the following:

• Get all Cat objects by passing the Cat class to the useQuery() hook,
and then use filtered() to filter the results to receive only the cats
whose names match the name passed as a prop. We then get the first
matching cat and store it as a const variable.

• Use dot notation to retrieve the Mixed property, birthDate.

• Display the cat's name and birthdate in the render method if Realm finds
the cat. If there is no cat that matches the name passed into the
component as a prop, we render text that says "Cat not found".
TypeScript
JavaScript
1 const CatInfoCard = ({catName}) => {
2 // To query for the cat's birthDate, filter for their name to retrieve the realm object.
3 // Use dot notation to access the birthDate property.
4 const cat = useQuery(Cat).filtered(`name = '${catName}'`)[0];
5 const catBirthDate = cat.birthDate;
6
7 if (cat) {
8 return (
9 <>
10 <Text>{catName}</Text>
11 <Text>{String(catBirthDate)}</Text>
12 </>
13 );
14 } else {
15 return <Text>Cat not found</Text>;
16 }
17 };

Mixed Properties and Type Checking

Because Mixed properties can be more than one type, you can't rely on the
property's value being a specific type.

With Object.getPropertyType(), you can get a Mixed property's underlying


type. This allows you build your own type checking.

// Use Type Predicates and Object.getPropertyType() to


// create a runtime type check for Mixed properties.
const isString = (
val: Mixed,
name: string,
object: Realm.Object,
): val is Realm.Types.String => {
return object.getPropertyType(name) === 'string';
};
type CatInfoCardProps = {catName: string};
const CatInfoCard = ({catName}: CatInfoCardProps) => {
const cat = useQuery(Cat).filtered(`name = '${catName}'`)[0];
// Use the type check to handle your data.
const catBirthDate = isString(cat.birthDate, 'birthDate', cat)
? cat.birthDate
: cat.birthDate.toString();
if (cat) {
return (
<>
<Text>{catName}</Text>
<Text>{catBirthDate}</Text>
</>
);
} else {
return <Text>Cat not found</Text>;
}
};

UUID - React Native SDK

Realm is now Atlas Device SDK – Learn More

New in version [email protected] .

UUID (Universal Unique Identifier) is a 16-byte unique value. UUID is bundled


with the Realm package as part of BSON (Realm.BSON.UUID).

You can use UUID as an unique identifier for objects. UUID is indexable, and
you can use it as a primary key.

TypeScript
JavaScript
class Profile extends Realm.Object {
static schema = {
name: 'Profile',
primaryKey: '_id',
properties: {
_id: 'uuid',
name: 'string',
},
};
}

Usage

To define a property as a UUID, set its type to "uuid" in your object model.
Create a Realm object within a write transaction. To set any unique identifier
properties of your object to a random value, call new UUID(). Alternatively,
pass a string to new UUID() to set the unique identifier property to a specific
value.

Example

In the following CreateProfileInput example, we create


a Profile Realm.Object with a uuid type for the _id field.

The CreateProfileInput component does the following:

• Gets access to the opened realm instance by calling


the useRealm() hook.
• Creates a name state variable
called "name" that represents the name of the profile.
• Creates a createProfile method that performs a write transaction. Within
that write transaction, we create a Profile object with the name value of the
"name" state variable and an _id value of a new UUID object.

• Renders a TextInput component that allows the user to enter a name for
the profile. When the user presses the "Create Profile" button,
the createProfile method is called and creates a Profile object.
TypeScript
JavaScript
1 const CreateProfileInput = () => {
2 const realm = useRealm();
3 const [name, setName] = useState('');
4
5 // createProfile creates a new 'Profile' Realm Object with a new UUID based on user input
6 const createProfile = () => {
7 realm.write(() => {
8 realm.create('Profile', {
9 name,
10 _id: new Realm.BSON.UUID(),
11 });
12 });
13 };
14 return (
15 <View>
16 <TextInput
17 placeholder='Name'
18 onChangeText={setName}
19 />
20 <Button
21 title='Create Profile'
22 onPress={createProfile}
23 />
24 </View>
25 );

Embedded Objects - React Native SDK

Realm is now Atlas Device SDK – Learn More

Create an Embedded Object

To create an embedded object, assign an instance of the embedded object to a


parent object's property.

Example

In the following CreateContact example, we create a new Contact object


with an embedded Address object.

The CreateContact component does the following:

• Creates React state variables that represent the contact's name and
address details.
• Gets access to an open realm instance by calling the useRealm() hook
within the component.

• Creates a component method submitContact() that performs a write


transaction to create a new Address embedded object
and Contact parent object based on the TextInput values for the
contact's name and address.

• Adds an onPress event on the "Submit Contact" button that


calls submitContact().
1 const CreateContact = () => {
2 const [name, setContactName] = useState('');
3 const [street, setStreet] = useState('');
4 const [city, setCity] = useState('');
5 const [country, setCountry] = useState('');
6 const [postalCode, setPostalCode] = useState('');
7 const realm = useRealm();
8
9 const submitContact = () => {
10 // Create a Contact within a write transaction
11 realm.write(() => {
12 // Create an embedded Address object
13 const address = {
14 street,
15 city,
16 country,
17 postalCode,
18 };
19
20 realm.create('Contact', {
21 _id: new Realm.BSON.ObjectID(),
22 name,
23 // Embed the address in the Contact object
24 address,
25 });
26 });
27 };
28 return (
29 <View>
30 <TextInput value={name} onChangeText={text => setContactName(text)} />
31 <TextInput value={street} onChangeText={text => setStreet(text)} />
32 <TextInput value={city} onChangeText={text => setCity(text)} />
33 <TextInput value={country} onChangeText={text => setCountry(text)} />
34 <TextInput
35 value={postalCode}
36 onChangeText={text => setPostalCode(text)}
37 />
38 <Button
39 title='Submit Contact'
40 onPress={submitContact}
41 />
42 </View>
43 );
44 };

Query a Collection on Embedded Object Properties

You can use dot notation to filter or sort a collection of objects based on an
embedded object property value.

Example

In the following ContactList example, we filter and query an


embedded Address object.

The ContactList component does the following:

• Performs a query for all contacts by passing the Contact class to


the useQuery hook.

• Filters for contacts with the name "John Smith" by


passing collection.filtered() on the query "name == 'John Smith'".

• Retrieves the contact's street address by using dot notation.


TypeScript
JavaScript
1 const ContactList = ({postalCode}) => {
2 // Query for all Contact objects
3 const contacts = useQuery(Contact);
4
5 // Run the `.filtered()` method on all the returned Contacts to get
6 // contacts with a specific postal code.
7 const contactsInArea = contacts.filtered(
8 `address.postalCode == '${postalCode}'`,
9 );
10
11 if (contactsInArea.length) {
12 return (
13 <>
14 <FlatList
15 testID='contactsList'
16 data={contactsInArea}
17 renderItem={({item}) => {
18 <Text>{item.name}</Text>;
19 }}
20 />
21 </>
22 );
23 } else {
24 return <Text>No contacts found in this area.</Text>;
25 }
26 };

Update an Embedded Object Property

To update a property in an embedded object, modify the property in a write


transaction.

Example

In the following UpdateContact example, we update the street property for


an embedded Address object.

The UpdateContact component does the following:

• Creates a React state variable that represents the contact's new street
address.

• Performs a query for all contacts by passing the Contact class to


the useQuery hook and filters for the contact that matches the name
passed into the component as a prop.

• Gets access to an opened realm instance by calling


the useRealm() hook within the component.

• Creates a component method updateStreet() that performs a write


transaction and sets the contact's street address to the value of
the street state variable.
• Renders a TextInput that displays and changes the street state
variable.

• Adds an onPress event on the 'Update Street Address' button that


calls updateStreet().
TypeScript
JavaScript
1 // Find the contact you want to update
2 const UpdateContact = ({contactId}) => {
3 const [street, setStreet] = useState('');
4 const contact = useObject(Contact, contactId);
5 const realm = useRealm();
6
7 const updateStreet = () => {
8 // Modify the property of the embedded Address object in a write transaction
9 realm.write(() => {
10 // Update the address directly through the contact
11 contact.address.street = street;
12 });
13 };
14
15 return (
16 <View>
17 <Text>{contact.name}</Text>
18 <TextInput
19 value={street}
20 onChangeText={setStreet}
21 placeholder='Enter New Street Address'
22 />
23 <Button
24 onPress={updateStreet}
25 title='Update Street Address'
26 />
27 </View>
28 );
29 };

Overwrite an Embedded Object

To overwrite an embedded object, reassign the embedded object property of a


party to a new instance in a write transaction.

Example
In the following OverwriteContact example, we overwrite an
embedded Address object.

The OverwriteContact component does the following:

• Creates React state variables that represent the contact's new address.

• Performs a query for all contacts by passing the Contact class to


the useQuery hook and filters for the contact that matches the name
passed into the component as a prop.

• Gets access to an opened realm instance by calling


the useRealm() hook within the component.

• Creates a component method updateAddress() that performs a write


transaction and creates a new Address object that overwrites the
existing address in the Contact object.

• Renders TextInput components that display and change the state


variables for the new address.

• Adds an onPress event on the 'Overwrite Address' button that


calls updateAddress().
TypeScript
JavaScript
1 const OverwriteContact = ({contactId}) => {
2 const [street, setStreet] = useState('');
3 const [city, setCity] = useState('');
4 const [country, setCountry] = useState('');
5 const [postalCode, setPostalCode] = useState('');
6 const contact = useObject(Contact, contactId);
7 const realm = useRealm();
8
9 const updateAddress = () => {
10 realm.write(() => {
11 // Within a write transaction, overwrite the embedded object with the new address
12 const address = {
13 street,
14 city,
15 country,
16 postalCode,
17 };
18
19 contact.address = address;
20 });
21 };
22 return (
23 <View>
24 <Text>{contact.name}</Text>
25 <Text>Enter the new address:</Text>
26 <TextInput
27 value={street}
28 onChangeText={setStreet}
29 placeholder='Street'
30 />
31 <TextInput value={city} onChangeText={setCity} placeholder='City' />
32 <TextInput
33 value={country}
34 onChangeText={setCountry}
35 placeholder='Country'
36 />
37 <TextInput
38 value={postalCode}
39 onChangeText={setPostalCode}
40 placeholder='Postal Code'
41 />
42 <Button
43 onPress={updateAddress}
44 title='Overwrite Address'
45 />
46 </View>
47 );
48 };

Delete an Embedded Object

Realm Uses Cascading Deletes for Embedded Objects. To delete an embedded


object, delete the embedded object's parent.

Example

In the following DeleteContact example, we delete an embedded object and


its parent object.

The DeleteContact component does the following:

• Performs a query for all contacts by passing the Contact class to


the useQuery hook.
• Filters for the Contact object that matches the name passed into the
component as a prop.

• Gets access to an open realm instance by calling the useRealm() hook


within the component.

• Creates a component method deleteContact() that performs a write


transaction and calls Realm.delete() to remove the Contact object.

• Add an onPress event on the "Delete Contact" button that


calls deleteContact().
TypeScript
JavaScript
1 const ContactInfo = ({contactCity, postalCode}) => {
2 const contacts = useQuery(Contact);
3 const parentsToDelete = contacts.filtered(
4 `address.city == '${contactCity}'`,
5 );
6 const embeddedToDelete = contacts.filtered(
7 `address.postalCode == '${postalCode}'`,
8 );
9 const realm = useRealm();
10
11 const deleteParentObject = () => {
12 realm.write(() => {
13 // Delete all objects that match the filter.
14 // Also deletes embedded objects.
15 realm.delete(parentsToDelete);
16 });
17 };
18
19 const deleteEmbeddedObject = () => {
20 realm.write(() => {
21 embeddedToDelete.forEach(contact => {
22 // Delete just the embedded object.
23 realm.delete(contact.address);
24 });
25 });
26 };
27
28 return (
29 <View>
30 <Text testID='contactCityText'>{contactCity}</Text>
31 <Button
32 onPress={deleteParentObject}
33 title='Delete Contact'
34 />
35 <Button
36 onPress={deleteEmbeddedObject}
37 title='Delete Address'
38 />
39 </View>
40 );
41 };

CRUD - React Native SDK

Realm is now Atlas Device SDK – Learn More

Within a RealmProvider, you can access a realm with the useRealm() hook.
Then, you can create Realm objects using a Realm.write() transaction block.

All operations within a write transaction are atomic. If an operation in the write
transaction fails, the whole transaction fails, Realm throws an error, and no
changes from the transaction block are applied to the realm.

Every write operation must occur in a write transaction.

const CreatePersonInput = () => {


const [name, setName] = useState('');
const realm = useRealm();
const handleAddPerson = () => {
realm.write(() => {
realm.create('Person', {_id: PERSON_ID, name: name, age: 25});
});
};
return (
<>
<TextInput value={name} onChangeText={setName} />
<Button
onPress={() => handleAddPerson()}
title='Add Person'
/>
</>
);
};

Transaction Lifecycle
A given realm only processes one write transaction at a time. When you make
a write transaction, the realm adds the transaction to a queue. The realm
evaluates each transaction in the order it arrived.

After processing a transaction, Realm either commits it or cancels it:

• After a commit, the realm applies all operations in the transaction. Once
applied, the realm automatically updates live queries. It notifies listeners
of created, modified, and deleted objects.

o When using Sync, the SDK also queues the changes to send to
Atlas App Services. The SDK sends these changes when a
network is available.

o After a commit, objects and collections returned


by useQuery or useObject rerender to include relevant changes.

• Realm does not apply any operations in a cancelled transaction. Realm


cancels a transaction if an operation fails or is invalid.

CRUD - Create - React Native SDK

Realm is now Atlas Device SDK – Learn More

To add a new Realm object to a realm instance, use realm.create() inside of a


write transaction. If the schema includes the object type and the object
conforms to the schema, then Realm stores the object.

The example for creating an object uses the following schema:

TypeScript
JavaScript
class Person extends Realm.Object{
static schema = {
name: 'Person',
primaryKey: '_id',
properties: {
_id: 'objectId',
name: 'string',
age: 'int?',
},
};
}

To create a new object:

1. Access a realm instance using the useRealm() hook.

2. Create handleAddPerson(), which creates a new Person object based


on the TextInput value.

3. Add an onPress event on the submit button that


calls handleAddPerson().
1 const CreatePersonInput = () => {
2 const [name, setName] = useState('');
3 const realm = useRealm();
4
5 const handleAddPerson = () => {
6 realm.write(() => {
7 realm.create('Person', {_id: PERSON_ID, name: name, age: 25});
8 });
9 };
10
11 return (
12 <>
13 <TextInput value={name} onChangeText={setName} />
14 <Button
15 onPress={() => handleAddPerson()}
16 title='Add Person'
17 />
18 </>
19 );
20 };

Create an Object with a To-One Relationship

In a one-to-one relationship, an object is related to at most one other object of


a particular type. To learn more about one-to-one relationships, refer to
to Relationships & Embedded Objects.
The example for creating an object with a to-one relationship uses the
following schema to indicate that a Pet Owner may only own one Pet:

TypeScript
JavaScript
class Pet extends Realm.Object {
static schema = {
name: 'Pet',
primaryKey: '_id',
properties: {
_id: 'objectId',
name: 'string',
age: 'int',
animalType: 'string?',
},
};
}
class PetOwner extends Realm.Object {
static schema = {
name: 'PetOwner',
primaryKey: '_id',
properties: {
_id: 'objectId',
name: 'string',
age: 'int',
pet: 'Pet?'
},
};
}

To create an object with a to-one relationship to another object:

1. Query the realm for a pre-existing Pet object. Assign the result
to newPet.

2. Create a new PetOwner object and pass newPet to the pet property.

3. Wrap your write transaction in a handleAddPetOwner() function,


which creates a new PetOwner object with an associated Pet.

4. Add an onPress event on the submit button that


calls handleAddPetOwner().
1 const CreatePetOwnerInput = () => {
2 const [ownerName, setOwnerName] = useState('')
3 const realm = useRealm();
4 const newPet = useObject(Pet, PET_ID);
5
6 const handleAddPetOwner = () => {
7 // Create a new Pet Owner object, pass new Pet object in pet field
8 realm.write(() => {
9 realm.create('PetOwner', {
10 _id: PETOWNER_ID,
11 name: ownerName,
12 age: 25,
13 pet: newPet
14 })
15 });
16 };
17
18 return (
19 <>
20 <TextInput onChangeText={setOwnerName} value={ownerName} />
21 <Button
22 onPress={() => handleAddPetOwner()}
23 title='Add New Pet Owner'
24 />
25 </>
26 );
27 }

Create an Object with a To-Many Relationship

In a one-to-many relationship, an object may be related to multiple objects of a


particular type. To learn more about one-to-many relationships, refer to
to Relationships & Embedded Objects.

The example for creating an object with a to-many relationship uses the
following schema to indicate that a Company may employ multiple Employees:

TypeScript
JavaScript
class Employee extends Realm.Object {
static schema = {
name: 'Employee',
primaryKey: '_id',
properties: {
_id: 'objectId',
name: 'string',
birthdate: 'date',
},
};
}
class Company extends Realm.Object {
static schema = {
name: 'Company',
primaryKey: '_id',
properties: {
_id: 'objectId',
name: 'string',
employees: {
type: 'list',
objectType: 'Employee',
optional: false,
},
},
};
}

To create an object with a to-many relationship to another object:

1. Query the realm for all pre-existing Employee objects using useQuery().

2. Create a new Company object and pass the results of your previous
query to the employees property.

3. Wrap your write transaction in a handleAddCompany() function, which


creates a new Company object with an associated list of Employees.

4. Add an onPress event on the submit button that


calls handleAddCompany().
1 const CreateNewCompanyInput = () => {
2 const employees = useQuery(Employee);
3 const [companyName, setCompanyName] = useState('');
4 const realm = useRealm();
5
6 // Create a new Company and connect our list of Employees to it
7 const handleCreateCompany = () => {
8 realm.write(() => {
9 realm.create('Company', {
10 _id: COMPANY_ID,
11 name: companyName,
12 employees: employees
13 })
14 });
15 }
16
17 return (
18 <>
19 <TextInput onChangeText={setCompanyName} value={companyName} />
20 <Button
21 onPress={() => handleCreateCompany()}
22 title='Add New Company'
23 />
24 </>
25 );
26 }

Create an Embedded Object

An embedded object is an object that exists as data nested inside of a parent


object; it cannot exist as an independent Realm object. To learn more about
embedded objects, refer to to Relationships & Embedded Objects.

The example for representing an embedded object uses the following schema
that allows you to embed a single Address into a new Contact object:

TypeScript
JavaScript
1 class Address extends Realm.Object {
2 static schema = {
3 name: "Address",
4 embedded: true, // default: false
5 properties: {
6 street: "string?",
7 city: "string?",
8 country: "string?",
9 postalCode: "string?",
10 },
11 };
12 }
1 class Contact extends Realm.Object {
2 static schema = {
3 name: "Contact",
4 primaryKey: "_id",
5 properties: {
6 _id: "objectId",
7 name: "string",
8 // Embed a single object
9 address: "Address",
10 },
11 };
12 }
1 class Business extends Realm.Object {
2 static schema = {
3 name: "Business",
4 primaryKey: "_id",
5 properties: {
6 _id: "objectId",
7 name: "string",
8 // Embed an array of objects
9 addresses: { type: "list", objectType: "Address" },
10 },
11 };
12 }

To create an embedded object, assign an instance of the embedded object to a


parent object's property.

The following CreateContact example creates a new Contact object with an


embedded Address object.

The CreateContact component does the following:

1. Creates React state variables that represent the contact's name and
address details.

2. Gets access to an open realm instance by calling the useRealm() hook


within the component.

3. Creates a component method submitContact() that performs a write


transaction to create a new Address embedded object
and Contact parent object based on the TextInput values for the
contact's name and address.

4. Adds an onPress event on the "Submit Contact" button that


calls submitContact().
1 const CreateContact = () => {
2 const [name, setContactName] = useState('');
3 const [street, setStreet] = useState('');
4 const [city, setCity] = useState('');
5 const [country, setCountry] = useState('');
6 const [postalCode, setPostalCode] = useState('');
7 const realm = useRealm();
8
9 const submitContact = () => {
10 // Create a Contact within a write transaction
11 realm.write(() => {
12 // Create an embedded Address object
13 const address = {
14 street,
15 city,
16 country,
17 postalCode,
18 };
19
20 realm.create('Contact', {
21 _id: new Realm.BSON.ObjectID(),
22 name,
23 // Embed the address in the Contact object
24 address,
25 });
26 });
27 };
28 return (
29 <View>
30 <TextInput value={name} onChangeText={text => setContactName(text)} />
31 <TextInput value={street} onChangeText={text => setStreet(text)} />
32 <TextInput value={city} onChangeText={text => setCity(text)} />
33 <TextInput value={country} onChangeText={text => setCountry(text)} />
34 <TextInput
35 value={postalCode}
36 onChangeText={text => setPostalCode(text)}
37 />
38 <Button
39 title='Submit Contact'
40 onPress={submitContact}
41 />
42 </View>
43 );
44 };

Create an Asymmetric Object

An asymmetric object allows you to sync a collection unidirectionally from


your decive to your Atlas database, if you are using Flexible Sync. To learn
more about asymmetric objects, refer to to Stream Data to Atlas.

The example for creating an asymmetric object uses the following schema that
defines a Weather Sensor object for sending weather-related data one-way
from your device to your Atlas database:

TypeScript
JavaScript
class WeatherSensor extends Realm.Object{
static schema = {
name: 'WeatherSensor',
// sync WeatherSensor objects one way from your device
// to your Atlas database.
asymmetric: true,
primaryKey: '_id',
properties: {
_id: 'objectId',
deviceId: 'string',
temperatureInFahrenheit: 'int',
barometricPressureInHg: 'float',
windSpeedInMph: 'float',
},
};
}

You can create an asymmetric object inside a write transaction


using realm.create(). When creating an asymmetric
object, Realm.create() returns undefined rather than the object itself.

const App = () => {


// Getting access to our opened realm instance
const realm = useRealm();
const handleAddSensor = () => {
realm.write(() => {
realm.create('WeatherSensor', {
_id: weatherSensorPrimaryKey,
deviceId: "WX1278UIT",
temperatureInFahrenheit: 66.7,
barometricPressureInHg: 29.65,
windSpeedInMph: 2,
});
});
}
return (
<Button
title='Add A New Sensor'
onPress={() => handleAddSensor()}
/>
)
};

CRUD - Read - React Native SDK


Realm is now Atlas Device SDK – Learn More

Read operations are queries to find your data stored in Realm. Use the
following @realm/react hooks to read data in a realm:

• useObject(): Find a specific object by primary key.

• useQuery(): Get a collection of objects by object type.

These hooks return live objects, which are automatically updated when the
data in the realm changes. When objects returned by these hooks are updated,
the component calling the hook rerenders.

The examples on this page use the following schemas:

TypeScript
JavaScript
class Person extends Realm.Object {
static schema = {
name: 'Person',
properties: {
name: 'string',
age: 'int?',
},
};
}
class Task extends Realm.Object {
static schema = {
name: 'Task',
properties: {
_id: 'int',
name: 'string',
priority: 'int?',
progressMinutes: 'int?',
assignee: 'Person?',
},
primaryKey: '_id',
};
}

Find a Specific Object by Primary Key

If you know the primary key for a given object, you can look it up directly by
passing the class type and primary key to the useObject() hook.
In the following example of a TaskItem component, we use
the useObject() hook to find a task based on its primary key: _id. Then we
render the task's name and priority in the UI.

TypeScript
JavaScript
const TaskItem = ({_id}) => {
const myTask = useObject(Task, _id);
return (
<View>
{myTask ? (
<Text>
{myTask.name} is a task with the priority of: {myTask.priority}
</Text>
) : null}
</View>
);
};

Query for an Object Type and Filter Results

The useQuery() hook returns a collection of Realm objects that match the
query as a Realm.Results object. A basic query matches all objects of a given
type in a realm, but you can also apply a filter to the collection to find specific
objects.

A filter selects a subset of results based on the value(s) of one or more object
properties. Realm lets you filter data using Realm Query Language, a string-
based query language to constrain searches when retrieving objects from a
realm.

Call filtered() on the query results collection to filter a query. Pass a Realm
Query Language query as an argument to filtered().

In the following example of a TaskList component, we:

1. Obtain all Task objects by passing "Task" to the useQuery() hook.

2. Obtain all high-priority tasks and low-progress task by passing a query


to filtered().
3. Use the map function to render a list of Text components displaying
information about the high-priority and low-progress tasks.
TypeScript
JavaScript
const TaskList = () => {
// retrieve the set of Task objects
const tasks = useQuery(Task);
// filter for tasks with a high priority
const highPriorityTasks = tasks.filtered('priority >= $0', 4);
// filter for tasks that have just-started or short-running progress
const lowProgressTasks = tasks.filtered(
'$0 <= progressMinutes && progressMinutes < $1',
1,
10,
);
return (
<>
<Text>Your high priority tasks:</Text>
{highPriorityTasks.map(taskItem => {
return <Text>{taskItem.name}</Text>;
})}
<Text>Your tasks without much progress:</Text>
{lowProgressTasks.map(taskItem => {
return <Text>{taskItem.name}</Text>;
})}
</>
);
};

TIP

Filter on Related and Embedded Object Properties

To filter a query based on a property of an embedded object or a related


object, use dot-notation as if it were in a regular, nested object.

TIP

See also:

• Realm Query Language Reference

• Query Data - React Native SDK


Query for an Object Type and Sort Results

A sort operation allows you to configure the order in which Realm returns
queried objects. You can sort based on one or more properties of the objects
in the results collection. Realm only guarantees a consistent order of results if
you explicitly sort them.

To sort a query, call the sorted() method on the query results collection.

In the following example of a TaskList component, we use


the useQuery() hook to initially retrieve the set of Task objects. We then use
the sorted() method to work with the data in various ways:

1. Sort objects based on the task's name alphabetically.

2. Sort objects based on the task's name alphabetically in descending


order.

3. Sort objects based on the task's priority in descending order and the
task's name in ascending order.

4. Sort objects based on the assignee object's name alphabetically.

Finally, we map through each list of tasks and render them in the UI.

TypeScript
JavaScript
const TaskList = () => {
// retrieve the set of Task objects
const tasks = useQuery(Task);
// Sort tasks by name in ascending order
const tasksByName = tasks.sorted('name');
// Sort tasks by name in descending order
const tasksByNameDescending = tasks.sorted('name', true);
// Sort tasks by priority in descending order and then by name alphabetically
const tasksByPriorityDescendingAndName = tasks.sorted([
['priority', true],
['name', false],
]);
// Sort Tasks by Assignee's name.
const tasksByAssigneeName = tasks.sorted('assignee.name');
return (
<>
<Text>All tasks:</Text>
{tasks.map(task => (
<Text>{task.name}</Text>
))}
<Text>Tasks sorted by name:</Text>
{tasksByName.map(task => (
<Text>{task.name}</Text>
))}
<Text>Tasks sorted by name descending:</Text>
{tasksByNameDescending.map(task => (
<Text>{task.name}</Text>
))}
<Text>
Tasks sorted by priority descending, and name alphabetically:
</Text>
{tasksByPriorityDescendingAndName.map(task => (
<Text>
{task.name}
</Text>
))}
<Text>Tasks sorted by assignee name:</Text>
{tasksByAssigneeName.map(task => (
<Text>{task.name}</Text>
))}
</>
);
};

TIP

Sort on Related and Embedded Object Properties

To sort a query based on a property of an embedded object or a related object,


use dot-notation as if it were in a regular, nested object.

CRUD - Update - React Native SDK

Realm is now Atlas Device SDK – Learn More

The examples on this page use the following schema:

TypeScript
JavaScript
class Task extends Realm.Object {
static schema = {
name: 'Task',
properties: {
_id: 'int',
name: 'string',
priority: 'int?',
progressMinutes: 'int?',
assignee: 'Person?',
},
primaryKey: '_id',
};
}

Update an Object

You can add, modify, or delete properties of a Realm object in the same way
that you would update any other JavaScript object. But, you must do it inside
of a write transaction.

In the following example of a TaskItem component, we:

1. Get access to the opened realm instance by calling


the useRealm() hook within the component.

2. Retrieve a task by calling useObject() with "Task" and


the _id parameter of the component.

3. Create a component method incrementTaskProgress() that performs


a write transaction and increments the task's progressMinutes.

4. Render the task's name and progressMinutes in the UI.

5. Add an onPress event on the "increment" button that


calls incrementTaskProgress().
TypeScript
JavaScript
const TaskItem = ({_id}) => {
const realm = useRealm();
const myTask = useObject(Task, _id);
const incrementTaskProgress = () => {
if (myTask) {
realm.write(() => {
myTask.progressMinutes += 1;
});
}
};
if (myTask) {
return (
<>
<Text>Task: {myTask.name}</Text>
<Text>Progress made (in minutes):</Text>
<Text>{myTask.progressMinutes}</Text>
<Button
onPress={() => incrementTaskProgress()}
title='Increment Task Progress'
/>
</>
);
} else {
return <></>;
}
};

TIP

Update Related and Embedded Objects

To update a property of an embedded object or a related object, modify the


property with dot-notation or bracket-notation as if it were in a regular, nested
object.

Upsert an Object

To upsert an object within a write transaction, call Realm.create() with the


update mode set to modified. The operation either inserts a new object with
the given primary key or updates an existing object that already has that
primary key.

NOTE

Upserting Requires Realm.create()

You must call Realm.create() within a write transaction to upsert an object.


This is different than creating a new Realm Objects by calling
the new operator.

In the following example of a CreateTaskItem component we:


1. Get access to the opened realm instance by calling
the useRealm() hook within the component.

2. Perform a write transaction, and create a Task object with an _id value
of 1234.

3. Call Realm.create() inside the write transaction to upsert


a Task object by specifying the same _id and a
different``progressMinutes`` and the update mode set to "modified".

4. Render the task's name and progressMinutes in the UI, showing the
modified progress.
TypeScript
JavaScript
const CreateTaskItem = () => {
const realm = useRealm();
let myTask;
realm.write(() => {
// Add a new Task to the realm. Since no task with ID 1234
// has been added yet, this adds the instance to the realm.
myTask = realm.create(
'Task',
{_id: 1234, name: 'Wash the car', progressMinutes: 0},
'modified',
);
// If an object exists, setting the third parameter (`updateMode`) to
// "modified" only updates properties that have changed, resulting in
// faster operations.
myTask = realm.create(
'Task',
{_id: 1234, name: 'Wash the car', progressMinutes: 5},
'modified',
);
});
return (
<>
<Text>{myTask.name}</Text>
<Text>Progress made (in minutes):</Text>
<Text>{myTask.progressMinutes}</Text>
</>
);
};

Bulk Update a Collection


To apply an update to a collection of objects, iterate through the collection
(e.g. with for...of). In the loop, update each object individually.

In the following example of a TaskDashboard component, we:

1. Get access to the opened realm instance by calling


the useRealm() hook within the component.

2. Retrieve all tasks in the realm instance by passing Task to


the useQuery() hook.

3. Create a component method resetProgressOnAllTasks() that


performs a write transaction. Within that write transaction, we bulk
update all tasks by looping through them using for...of and set
their progressMinutes to 0.

4. Map through the tasks to render a list of Text components displaying


each task's name and progressMinutes.
const TaskDashboard = () => {
const realm = useRealm();
const tasks = useQuery(Task);
const resetProgressOnAllTasks = () => {
realm.write(() => {
for (const task of tasks) {
task.progressMinutes = 0;
}
});
};
return (
<>
{tasks.map(task => {
<Text>
{task.name} has {task.progressMinutes} minutes progressed
</Text>;
})}
<Button
onPress={resetProgressOnAllTasks}
title='Reset Progress'
/>
</>
);
};
CRUD - Delete - React Native SDK

Realm is now Atlas Device SDK – Learn More

The examples on this page use the following schema:

TypeScript
JavaScript
class Dog extends Realm.Object {
static schema = {
name: 'Dog',
properties: {
name: 'string',
owners: {
type: 'list',
objectType: 'Person',
optional: true,
},
age: 'int?',
},
};
}

Delete an Object

To delete an object from a realm, pass the object to Realm.delete() inside of a


write transaction.

In the following example of a DogList component, we:

1. Get access to the opened realm instance by calling


the useRealm() hook.

2. Retrieve all dogs in the realm instance by passing Dog to


the useQuery() hook.

3. Create a component method deleteDog() that takes in a Dog object as


a parameter. Within the method, we
pass Realm.delete() the Dog object, deleting it from the realm.

4. Map through the dogs to render a list of Text components that contain
a dog's name and a "Delete Dog" button.
5. Add an onPress event on the "Delete Dog" button that calls the
component's deleteDog() method.
TypeScript
JavaScript
const DogList = () => {
const realm = useRealm();
const myDogs = useQuery(Dog);
const deleteDog = deletableDog => {
realm.write(() => {
realm.delete(deletableDog);
});
};
return (
<>
{myDogs.map(dog => {
return (
<>
<Text>{dog.name}</Text>
<Button
onPress={() => deleteDog(dog)}
title='Delete Dog'
/>
</>
);
})}
</>
);
};

IMPORTANT

Do not use objects after delete

You cannot access or modify an object after you have deleted it from a Realm.
If you try to use a deleted object, Realm throws an error.

Delete Multiple Objects

You can delete multiple objects from a realm in a couple of ways:

1. To delete all objects of a given object type from a realm, pass the results
of useQuery(<ObjectType>) to the Realm.delete() method inside of a
write transaction.
2. To delete many specific objects from a realm,
pass Collection.filtered() to Realm.delete() inside of a write
transaction.

In the following example of a DogList component, we:

1. Retrieve the realm instance using the useRealm() hook.

2. Set a variable myDogs to all the Dog objects by passing the Dog class to
the useQuery() hook.

3. Create a component method deleteAllYoungDogObjects() that


performs a write transaction. Within the write transaction, we set a
variable, youngDogs, to the result of myDogs.filtered() with a query
to obtain all dogs younger than three. Then
pass youngDogs to realm.delete(), deleting all young dogs from the
realm.

4. Create a component method deleteAllDogObjects() that performs a


write transaction. Within the write transaction, we
pass myDogs to realm.delete(), deleting all the dogs from the realm.

5. Map through the dogs to render a list of Text components that contain
a dog's name and age.

6. Add an onPress event on the "Delete Young Dog Objects" button that
calls deleteAllYoungDogObjects(), deleting all young dogs from the
realm, which triggers a re-render and removes them from the UI.

7. Add an onPress event on the "Delete All Dog Objects" button that
calls deleteAllDogObjects(), deleting every dog from the realm,
which triggers a re-render and removes them from the UI.

NOTE

When you delete objects from the realm instance, the component
automatically re-renders and removes them from the UI.
TypeScript
JavaScript
const DogList = () => {
const realm = useRealm();
const myDogs = useQuery(Dog);
const deleteAllYoungDogObjects = () => {
const youngDogs = myDogs.filtered('age < 3');
realm.write(() => {
realm.delete(youngDogs);
});
};
const deleteAllDogObjects = () => {
realm.write(() => {
realm.delete(myDogs);
});
};
return (
<>
{myDogs.map(dog => {
return (
<>
<Text>{dog.name}</Text>
<Text>{dog.age}</Text>
</>
);
})}
<Button
onPress={() => deleteAllYoungDogObjects()}
title='Delete Young Dog Objects'
/>
<Button
onPress={() => deleteAllDogObjects()}
title='Delete All Dog Objects'
/>
</>
);
};

Delete All Objects in a Realm

To delete all objects from the realm, call Realm.deleteAll() inside of a write
transaction. This clears the realm of all object instances but does not affect
the realm's schema.

In the following example of a DeleteProfileSettingsScreen component,


we:
1. Get access to the opened realm instance by calling
the useRealm() hook within the component.

2. Create a component method deleteAllData() that performs a write


transaction and calls Realm.deleteAll(), deleting all objects from the
realm.

3. Add an onPress event on the "Delete all data" button that


calls deleteAllData().
TypeScript
JavaScript
const DeleteProfileSettingsScreen = () => {
const realm = useRealm();
const deleteAllData = () => {
realm.write(() => {
realm.deleteAll();
});
};
return (
<>
<Text>Delete all data in your profile:</Text>
<Button
onPress={deleteAllData}
title='Delete all data'
/>
</>
);
};

TIP

Delete All In Development

Realm.deleteAll() is a useful method to quickly clear out your realm in the


course of development. For example, rather than writing a migration to update
objects to a new schema, it may be faster to delete and then re-generate the
objects with the app itself.

Query Data - React Native SDK


Realm is now Atlas Device SDK – Learn More

To filter data in your Realms, construct queries with Realm Query Language.
For more information about syntax, usage and limitations, refer to the Realm
Query Language reference.

Examples on This Page

The examples in this page use a simple data set for a task list app. The two
Realm object types are Project and Task. A Task has a name, assignee's
name, and completed flag. There is also an arbitrary number for priority
(higher is more important) and a count of minutes spent working on it.
A Project has zero or more Tasks.

See the schema for these two classes, Project and Task:

const TaskSchema = {
name: "Task",
properties: {
name: "string",
isComplete: "bool",
priority: "int",
progressMinutes: "int",
assignee: "string?"
}
};
const ProjectSchema = {
name: "Project",
properties: {
name: "string",
tasks: "Task[]"
}
};

Construct a Query

To filter data, pass a query made with Realm Query Language


to Realm.Results.filtered()

const items = realm.objects("Item");


// Gets all items where the 'priority' property is 7 or more.

const importantItems = items.filtered("priority >= $0", 7);

Filter with Full-Text Search

You can use Realm Query Language (RQL) to query on properties that have
a Full-Text Search (FTS) index. To begin, use useQuery() to return the
collection of realm objects you'd like to filter. To query the FTS indexed
property for objects within the collection, use the TEXT predicate in
your filtered query.

Exclude results for a word by placing the - character in front of the word. For
example, a search for swan -lake would include all search results
for swan excluding those with lake.

In the following example, we query on the Book.name field using the


following Book object model:

class Book extends Realm.Object {


name!: string;
price?: number;
static schema: ObjectSchema = {
name: 'Book',
properties: {
name: {type: 'string', indexed: 'full-text'},
price: 'int?',
},
};
}
import {useQuery} from "@realm/react";
// Retrieve book objects from realm
const books = useQuery(Book);
// Filter for books with 'hunger' in the name
const booksWithHunger = books.filtered("name TEXT $0", "hunger");
// Filter for books with 'swan' but not 'lake' in the name
const booksWithSwanWithoutLake = books.filtered("name TEXT $0", "swan -lake");

Full-Text Search Tokenizer Details

Full-Text Search (FTS) indexes support:

• Boolean match word searches, not searches for relevance.

• Tokens are diacritics- and case-insensitive.

• Tokens can only consist of characters from ASCII and the Latin-1
supplement (western languages).

• All other characters are considered whitespace. Words split by a hyphen


(-) like full-text are split into two tokens.

Operators

There are several types of operators available to filter a Realm collection.


Filters work by evaluating an operator expression for every object in the
collection being filtered. If the expression resolves to true, Realm Database
includes the object in the results collection.

An expression consists of one of the following:


• The name of a property of the object currently being evaluated.

• An operator and up to two argument expression(s).

• A literal string, number, or date.

Comparison Operators

The most straightforward operation in a search is to compare values. Realm


Query Language has standard comparison operators like ==, >, >=, in, <, <=,
and !=.

For complete documentation on comparison operators, refer to the Realm


Query Language comparison operator reference.

The following example uses the query engine's comparison operators to:

• Find high priority tasks by comparing the value of


the priority property value with a threshold number, above which
priority can be considered high.

• Find just-started or short-running tasks by seeing if


the progressMinutes property falls within a certain range.

• Find unassigned tasks by finding tasks where the assignee property is


equal to null.

• Find tasks assigned to specific teammates Ali or Jamie by seeing if


the assignee property is in a list of names.
const highPriorityTasks = tasks.filtered("priority > $0", 5);
const unassignedTasks = tasks.filtered("assignee == $0", null);
const lowProgressTasks = tasks.filtered("$0 <= progressMinutes && progressMinutes < $1", 1, 10);
const aliTasks = tasks.filtered("assignee == $0", "Ali");
console.log(
`Number of high priority tasks: ${highPriorityTasks.length}`,
`Number of unassigned tasks: ${unassignedTasks.length}`,
`Number of just-started or short-running tasks: ${lowProgressTasks.length}`,
`Number of tasks for Ali: ${aliTasks.length}`
);

Logical Operators

Create compound predicates using logical operators. Realm Query Language


has standard logical operators like AND, OR, and NOT.

For complete documentation on logical operators, refer to the Realm Query


Language logical operator reference.

The following example uses Realm Query Language's logical operators to find
all of Ali's completed tasks. We find all tasks where the assignee property
value is equal to 'Ali' AND the isComplete property value is true.

console.log(
"Number of Ali's complete tasks: " +
tasks.filtered("assignee == $0 && isComplete == $1", "Ali", true).length
);

String Operators

You can compare string values using string operators


like ==, beginsWith, contains, and endsWith. You can also use
the LIKE operator to search with regex-like wildcards.

For complete documentation on string operators, refer to the Realm Query


Language string operator reference.

The following example uses Realm Query Language's string operators to find
projects with a name starting with the letter 'e' and projects with names that
contain 'ie'.
// Use [c] for case-insensitivity.
console.log(
"Projects that start with 'e': " +
projects.filtered("name BEGINSWITH[c] $0", 'e').length
);
console.log(
"Projects that contain 'ie': " +
projects.filtered("name CONTAINS $0", 'ie').length
);

Aggregate Operators

Traverse a collection and reduce it to a single value with an aggregate


operator.

For complete documentation on aggregate operators, refer to the Realm Query


Language aggregate operator reference.

The following examples uses aggregate operators to show different facets of


the data:

• @avg to show projects with average tasks priority above 5.

• @sum to show long running projects.


console.log(
"Number of projects with average tasks priority above 5: " +
projects.filtered("[email protected] > $0", 5).length
);
console.log(
"Number of long-running projects: " +
projects.filtered("[email protected] > $0", 120).length
);
Collection Operators

A collection operator uses rules to determine whether to pass each input


collection object to the output collection by applying a given predicate to
every element of a given list property of the object.

For complete documentation on collection operators, refer to the Realm Query


Language collection operator reference.

The following examples uses Realm Query Language's collection operators to


find:

• ALL for projects with no complete tasks.

• ANY for projects with any top priority tasks.


console.log(
"Number of projects with no complete tasks: " +
projects.filtered("ALL tasks.isComplete == $0", false).length
);
console.log(
"Number of projects with any top priority tasks: " +
projects.filtered("ANY tasks.priority == $0", 10).length
);

Summary

• Use Realm.Results.filtered to filter data in your realm using Realm Query


Language.

• For a detailed explanation of Realm Query Language, refer to the Realm


Query Language reference.

• There are several categories of operators available to filter results: -


comparison - logical - string - aggregate - set
React to Changes - React Native SDK

Realm is now Atlas Device SDK – Learn More

Register a Realm Change Listener

To register a change listener for an entire realm, pass a callback function to


the realm's addListener() method. Realm calls the listener asynchronously
whenever an operation adds, changes, or removes objects in the realm.

To remove a realm listener, pass the callback to the


realm's removeListener() method.

TIP

Use Object & Collection Listeners for Change Details

Realm does not pass any information about what changed to realm listener
callback functions. If you need to know more information about what changed
in an object or collection, use object listeners and collection listeners.

TIP

Handling Exceptions Inside a Listener

To handle exceptions thrown from a change listener, wrap


your addListener() call within a try...catch statement.
// Define a listener callback function
function onRealmChange() {
console.log("Something changed!");
}
// Add the listener callback to the realm
try {
realm.addListener("change", onRealmChange);
} catch (error) {
console.error(
`An exception was thrown within the change listener: ${error}`
);
}
// Remember to remove the listener when you're done!
realm.removeListener("change", onRealmChange);

Register a Collection Change Listener

To register a change listener for a collection of Realm objects, pass a callback


function to the collection's addListener() method. Realm calls the listener
asynchronously when it's registered as well as whenever an operation adds,
changes, or removes objects in the collection.

To remove a collection listener, pass the callback to the


collection's removeListener() method.

IMPORTANT

Order Matters

In collection notification handlers, always apply changes in the following


order: deletions, insertions, then modifications. Handling insertions before
deletions may result in unexpected behavior.
// You can define a listener for any collection of Realm objects
const dogs = realm.objects("Dog");
// Define a listener callback function for changes to any Dog
function onDogsChange(dogs, changes) {
// Handle deleted Dog objects
changes.deletions.forEach((index) => {
// You cannot directly access deleted objects,
// but you can update a UI list, etc. based on the index.
console.log(`Looks like Dog #${index} has left the realm.`);
});
// Handle newly added Dog objects
changes.insertions.forEach((index) => {
const insertedDog = dogs[index];
console.log(`Welcome our new friend, ${insertedDog.name}!`);
});
// Handle Dog objects that were modified
changes.modifications.forEach((index) => {
const modifiedDog = dogs[index];
console.log(`Hey ${modifiedDog.name}, you look different!`);
});
}
// Add the listener callback to the collection of dogs
try {
dogs.addListener(onDogsChange);
} catch (error) {
console.error(
`An exception was thrown within the change listener: ${error}`
);
}
// Remember to remove the listener when you're done!
dogs.removeListener(onDogsChange);

Register an Object Change Listener

To register a change listener on a specific Realm object, pass a callback


function to the object's addListener() method. Realm calls the listener if any of
the object's properties change or if someone deletes the object.

To remove an object listener, pass the callback to the


object's removeListener() method.

// Define a listener callback function for changes to a specific Dog


function onDogChange(dog, changes) {
if (changes.deleted) {
console.log(`dog is deleted: ${changes.deleted}`);
} else {
changes.changedProperties.forEach((prop) => {
console.log(`* the value of "${prop}" changed to ${dog[prop]}`);
});
}
}
// You can define a listener for any Realm object
try {
dog.addListener(onDogChange);
} catch (error) {
console.error(
`An exception was thrown within the change listener: ${error}`
);
}
// Remember to remove the listeners when you're done!
dog.removeListener(onDogChange);

Remove All Change Listeners

To remove all listeners on a given realm, object, or collection instance, call the
instance's removeAllListeners() function:
• Realm.removeAllListeners()

• Realm.Collection.removeAllListeners()

• Realm.Object.removeAllListeners()
// Remove all listeners from a realm
realm.removeAllListeners();
// Remove all listeners from a collection
dogs.removeAllListeners();
// Remove all listeners from an object
dog.removeAllListeners();

App Services - React Native SDK

Realm is now Atlas Device SDK – Learn More

Realm provides SDKs that help you connect your client apps to the Atlas App
Services backend. The SDK provides the functionality needed to authenticate
users with any of the built-in authentication providers, call backend functions,
and directly access a linked MongoDB data source.

The App Client

When using the SDK to access the App Services backend, you start with an
App object. This object provides all other functionality related to App Services.
The``App`` object is initialized with the App ID, which you can find in the App
Services UI.

TIP

To learn how to initialize the App client, see Connect to an Atlas App Services
App - React Native SDK.

Authentication & User Management


One of the most challenging aspects of client development is implementing a
robust and secure authentication system. With the Realm SDKs, however, you
can use any of the App Services authentication providers with very minimal
backend setup or client-side code required. With the authentication APIs, you
can implement the following functionality:

• Creation of new user accounts

• User log-in and log-out

• Multiple users logged on at the same time on the same device

• Linking user accounts from different providers

• Providing custom user data

TIP

To learn how to set up authentication with different providers,


see Authenticate Users - React Native SDK.

To learn how to manage multiple users, see Multi-User Applications - React


Native SDK.

To learn how to link user accounts, see Link User Identities - React Native
SDK.

To learn how to provide custom user data, see Custom User Data - React
Native SDK.

Calling Atlas Functions

Atlas Functions enable you to define and execute server-side logic for your
application. You can call these functions from your client applications via the
Realm SDKs. These server-side functions can run under the context of the
authenticated user, and thus honor the rules, roles, and permissions that you
have assigned to your collections.
By using Functions, you provide a secure way for a variety of client
applications to share complex functionality without having to reproduce that
logic client-side.

TIP

To learn how to call Functions, see Call a Function.

Accessing MongoDB

The Realm SDKs include APIs for accessing a MongoDB Atlas instance
directly. With these APIs, you can perform all of the standard CRUD operations
from your client. For security, you configure server-side data access rules to
dynamically determine read & write permissions for every object that is
accessed.

TIP

To learn how to use the MongoDB APIs, see Query MongoDB.

Connect to an Atlas App Services App - React Native SDK

Realm is now Atlas Device SDK – Learn More

The App client is the interface to the App Services backend. It provides access
to the authentication functionality, Atlas Functions, and Atlas Device Sync.

Before You Begin

1. Create an App Services App

2. Get Your App ID

Configure the App Client


To set up your App client, pass the App ID string to the id prop of
the AppProvider. Wrap any components that need to access the App with
the AppProvider.

import React from 'react';


import {AppProvider} from '@realm/react';
function AppWrapper() {
return (
<View>
<AppProvider id={APP_ID}>
<MyApp />
</AppProvider>
</View>
);
}

You can create multiple App client instances to connect to multiple Apps. All
App client instances that share the same App ID use the same underlying
connection.

IMPORTANT

You Can't Change an App Config After Initializing the App

When you initialize the App client, the configuration is cached internally.
Attempting to "close" an App and then re-open it with a changed configuration
within the same process has no effect. The client continues to use the cached
configuration.

Retrieve an Instance of the App Client

All components wrapped within an AppProvider can access the App client
with the useApp() hook. Using the App, you can authenticate users and
access App Services.

import React from 'react';


import {useApp} from '@realm/react';
import {Credentials} from 'realm';
function MyApp() {
const app = useApp();
function logInAnonymousUser() {
app.logIn(Credentials.anonymous());
}
// ...
}

Retrieve App Outside the App Provider

To retrieve an instance of the App Client from anywhere in your application,


instantiate a new instance of Realm.App() from the realm package, then pass
in your App ID.

import Realm from 'realm';


const app = Realm.App.getApp("<Your App ID>");

Call a Function - React Native SDK

Realm is now Atlas Device SDK – Learn More

The examples in this section demonstrate calling a simple Atlas Function


named sum that takes two arguments, adds them, and returns the result:

// sum: adds two numbers


exports = function(a, b) {
return a + b;
};

Before You Begin

1. In an App Services App, define an Atlas Function.

2. In your client project, initialize the App client.

3. Then, authenticate a user in your React Native project.

Call a Function

IMPORTANT
Make sure to sanitize client data to protect against code injection when using
Functions.

To call a function, you can either pass its name and arguments
to User.callFunction() or call the function as if it were a method on
the User.functions property.

import React from 'react';


import {useUser} from '@realm/react';
function Addition() {
// Get currently logged in user
const user = useUser();
const addNumbers = async (numA: number, numB: number) => {
// Call Atlas Function
// Method 1: call with User.callFunction()
const sumMethod1 = await user?.callFunction('sum', numA, numB);
// Method 2: Call with User.function.<Function name>()
const sumMethod2 = await user?.functions.sum(numA, numB);
// Both methods return the same result
console.log(sumMethod1 === sumMethod2); // true
};
// ...
}

Query MongoDB - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can query data stored in MongoDB Atlas directly from your client
application code by using the Realm React Native SDK's MongoDB client with
the Query API. Atlas App Services provides data access rules on collections to
securely retrieve results based on the logged-in user or the content of each
document.

NOTE

Example Dataset
The examples on this page use a MongoDB collection that describes inventory
in a chain of plant stores. For more information on the collection schema and
document contents, see Example Data.

Use Cases

There are a variety of reasons you might want to query a MongoDB data
source. Working with data in your client via Atlas Device Sync is not always
practical or possible. You might want to query MongoDB when:

• The data set is large or the client device has constraints against loading
the entire data set

• You are creating or updating custom user data

• You are retrieving documents that are not modeled in Realm

• Your app needs to access collections that don't have strict schemas

• A non-Realm service generates collections that you want to access

While not exhaustive, these are some common use cases for querying
MongoDB directly.

Prerequisites

Before you can query MongoDB from your React Native application, you must
set up MongoDB Data Access in your App Services App. To learn how to set
up your backend App to let the Realm SDK query Atlas, refer to Set Up
MongoDB Data Access in the App Services documentation.

EXAMPLE

Example Data

The examples on this page use the following MongoDB collection that
describes various plants for sale in a chain of plant stores:
{ _id: ObjectId("5f87976b7b800b285345a8c4"), name: "venus flytrap", sunlight: "full", color: "whi
{ _id: ObjectId("5f87976b7b800b285345a8c5"), name: "sweet basil", sunlight: "partial", color: "gre
{ _id: ObjectId("5f87976b7b800b285345a8c6"), name: "thai basil", sunlight: "partial", color: "gree
{ _id: ObjectId("5f87976b7b800b285345a8c7"), name: "helianthus", sunlight: "full", color: "yellow
{ _id: ObjectId("5f87976b7b800b285345a8c8"), name: "petunia", sunlight: "full", color: "purple",

Documents in the plants collection use the following schema:

JSON Schema
TypeScript
{
"title": "Plant",
"bsonType": "object",
"required": ["_id", "_partition", "name"],
"properties": {
"_id": { "bsonType": "objectId" },
"_partition": { "bsonType": "string" },
"name": { "bsonType": "string" },
"sunlight": { "bsonType": "string" },
"color": { "bsonType": "string" },
"type": { "bsonType": "string" }
}
}

Connect to a Linked Cluster

To access a linked cluster from your client application, authenticate a user and
pass the cluster name to User.mongoClient(). This returns a MongoDB service
interface that you can use to access databases and collections in the cluster.

If you are using @realm/react, you can access the MongoDB client with
the useUser() hook in a component wrapped by UserProvider.

TypeScript
JavaScript
import React from 'react';
import {useUser} from '@realm/react';
function QueryPlants() {
// Get currently logged in user
const user = useUser();
const getPlantByName = async name => {
// Access linked MongoDB collection
const mongodb = user.mongoClient('mongodb-atlas');
const plants = mongodb.db('example').collection('plants');
// Query the collection
const response = await plants.findOne({name});
return response;
};
// ...
}

Read Operations

Find a Single Document

To find a single document, pass a query that matches the document


to collection.findOne(). If you do not pass a query, findOne() matches the
first document it finds in the collection.

The following snippet finds the document that describes "venus flytrap" plants
in the collection of documents that describe plants for sale in a group of
stores:

const venusFlytrap = await plants.findOne({ name: "venus flytrap" });


console.log("venusFlytrap", venusFlytrap);
HIDE OUTPUT
{
_id: ObjectId("5f87976b7b800b285345a8c4"),
name: "venus flytrap",
sunlight: "full",
color: "white",
type: "perennial",
_partition: "Store 42",
}

Find Multiple Documents

To find multiple documents, pass a query that matches the documents


to collection.find(). If you do not pass a query, find() matches all documents
in the collection.

The following snippet finds all documents that describe perennial plants in
the collection of documents that describe plants for sale in a group of stores:

const perennials = await plants.find({ type: "perennial" });


console.log("perennials", perennials);
HIDE OUTPUT
[
{ _id: ObjectId("5f87976b7b800b285345a8c4"), name: 'venus flytrap', sunlight: 'full', color: 'white',
{ _id: ObjectId("5f87976b7b800b285345a8c6"), name: 'thai basil', sunlight: 'partial', color: 'green',
{ _id: ObjectId("5f879f83fc9013565c23360e"), name: 'lily of the valley', sunlight: 'full', color: 'white
{ _id: ObjectId("5f87a0defc9013565c233611"), name: 'rhubarb', sunlight: 'full', color: 'red', type: 'p
{ _id: ObjectId("5f87a0dffc9013565c233612"), name: 'wisteria lilac', sunlight: 'partial', color: 'purp
{ _id: ObjectId("5f87a0dffc9013565c233613"), name: 'daffodil', sunlight: 'full', color: 'yellow', type:
]

Count Documents

To count documents, pass a query that matches the documents


to collection.count(). If you do not pass a query, count() counts all
documents in the collection.

The following snippet counts the number of documents in a collection of


documents that describe plants for sale in a group of stores:

const numPlants = await plants.count();


console.log(`There are ${numPlants} plants in the collection`);
HIDE OUTPUT
"There are 9 plants in the collection"

Write Operations

Insert a Single Document

To insert a single document, pass it to collection.insertOne().

The following snippet inserts a single document describing a "lily of the


valley" plant into a collection of documents that describe plants for sale in a
group of stores:

const result = await plants.insertOne({


name: "lily of the valley",
sunlight: "full",
color: "white",
type: "perennial",
_partition: "Store 47",
});
console.log(result);
HIDE OUTPUT
{
insertedId: "5f879f83fc9013565c23360e",
}

Insert Multiple Documents

To insert multiple documents at the same time, pass them as an array


to collection.insertMany().

The following snippet inserts three documents describing plants into


a collection of documents that describe plants for sale in a group of stores:

const result = await plants.insertMany([


{
name: "rhubarb",
sunlight: "full",
color: "red",
type: "perennial",
_partition: "Store 47",
},
{
name: "wisteria lilac",
sunlight: "partial",
color: "purple",
type: "perennial",
_partition: "Store 42",
},
{
name: "daffodil",
sunlight: "full",
color: "yellow",
type: "perennial",
_partition: "Store 42",
},
]);
console.log(result);
HIDE OUTPUT
{
insertedIds: [
"5f87a0defc9013565c233611",
"5f87a0dffc9013565c233612",
"5f87a0dffc9013565c233613",
],
}

Update a Single Document

To update a single document, pass a query that matches the document and an
update document to collection.updateOne().

The following snippet updates a single document in a collection of documents


that describe plants for sale in a group of stores. This operation queries for a
document where the name field contains the value "petunia" and changes the
value of the first matched document's sunlight field to "partial":

const result = await plants.updateOne(


{ name: "petunia" },
{ $set: { sunlight: "partial" } }
);
console.log(result);
HIDE OUTPUT
{ matchedCount: 1, modifiedCount: 1 }

Update Multiple Documents

To update multiple documents simultaneously, pass a query that matches the


documents and an update description to collection.updateMany().

The following snippet updates multiple documents in a collection of


documents that describe plants for sale in a group of stores. This operation
queries for documents where the _partition field contains the value "Store
47" and changes the value of the _partition field of each matching
document to "Store 51":

const result = await plants.updateMany(


{ _partition: "Store 47" },
{ $set: { _partition: "Store 51" } }
);
console.log(result);
HIDE OUTPUT
{ matchedCount: 3, modifiedCount: 3 }
Upsert Documents

To upsert a document, set the upsert option to true in your update


operation. If the operation's query does not match any document in the
collection, an upsert automatically inserts a single new document into the
collection that matches the provided query document with the update applied
to it.

The following snippet updates a document in a collection of documents that


describe plants for sale in a group of stores with an upsert operation. The
query doesn't match any existing documents, so MongoDB automatically
creates a new one.

const result = await plants.updateOne(


{
sunlight: "full",
type: "perennial",
color: "green",
_partition: "Store 47",
},
{ $set: { name: "sweet basil" } },
{ upsert: true }
);
console.log(result);
HIDE OUTPUT
{
matchedCount: 0,
modifiedCount: 0,
upsertedId: ObjectId("5f1f63055512f2cb67f460a3"),
}

Delete a Single Document

To delete a single document from a collection, pass a query that matches the
document to collection.deleteOne(). If you do not pass a query or if the query
matches multiple documents, then the operation deletes the first document it
finds.

The following snippet deletes one document in a collection of documents that


describe plants for sale in a group of stores. This operation queries for a
document where the color field has a value of "green" and deletes the first
document that matches the query:

const result = await plants.deleteOne({ color: "green" });


console.log(result);
HIDE OUTPUT
{ deletedCount: 1 }

Delete Multiple Documents

To delete multiple document from a collection, pass a query that matches the
documents to collection.deleteMany(). If you do not pass a
query, deleteMany() deletes all documents in the collection.

The following snippet deletes all documents for plants that are in "Store 51" in
a collection of documents that describe plants for sale in a group of stores:

const result = await plants.deleteMany({


_partition: "Store 51",
});
console.log(result);
HIDE OUTPUT
{ deletedCount: 3 }

Real-time Change Notifications

You can call collection.watch() to subscribe to real-time change notifications


that MongoDB emits whenever a document in the collection is added,
modified, or deleted. Each notification specifies a document that changed,
how it changed, and the full document after the operation that caused the
event.

collection.watch() returns an async generator that allows you to


asynchronously pull change events for operations as they occur.

collection.watch() requires some set up to work with a React Native client


app. To watch a collection for changes, you must first install the react-native-
polyfill-globals and @babel/plugin-proposal-async-generator-
functions packages.

To use collection.watch():

1. Install dependencies.
npm install react-native-polyfill-globals text-encoding
npm install --save-dev @babel/plugin-proposal-async-generator-functions

2. Import polyfills in a higher scope than where you need to use them. For
example, in index.js.
import { polyfill as polyfillReadableStream } from "react-native-polyfill-globals/src/readable-
import { polyfill as polyfillEncoding } from "react-native-polyfill-globals/src/encoding";
import { polyfill as polyfillFetch } from "react-native-polyfill-globals/src/fetch";
polyfillReadableStream();
polyfillEncoding();
polyfillFetch();

IMPORTANT

Serverless Limitations

You cannot watch for changes if the data source is an Atlas serverless
instance. MongoDB serverless currently does not support change streams,
which are used on watched collections to listen for changes.

Watch for All Changes in a Collection

With the @realm/react package, make sure you have configured user
authentication for your app.

To watch for all changes in a collection, call collection.watch() with no


arguments. This call must be wrapped by a UserProvider with an
authenticated user.

TypeScript
JavaScript
import React, {useEffect} from 'react';
import Realm from 'realm';
import {useUser, useApp, AppProvider, UserProvider} from '@realm/react';
function AppWrapper() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={<LogIn />}>
<NotificationSetter />
</UserProvider>
</AppProvider>
);
}
function NotificationSetter() {
// Get currently logged in user
const user = useUser();
const watchForAllChanges = async (
plants,
) => {
// Watch for changes to the plants collection
for await (const change of plants.watch()) {
switch (change.operationType) {
case 'insert': {
const {documentKey, fullDocument} = change;
// ... do something with the change information.
break;
}
case 'update': {
const {documentKey, fullDocument} = change;
// ... do something with the change information.
break;
}
case 'replace': {
const {documentKey, fullDocument} = change;
// ... do something with the change information.
break;
}
case 'delete': {
const {documentKey} = change;
// ... do something with the change information.
break;
}
}
}
};
useEffect(() => {
const plants = user
.mongoClient('mongodb-atlas')
.db('example')
.collection('plants');
// Set up notifications
watchForAllChanges(plants);
}, [user, watchForAllChanges]);
// ... rest of component
}

Watch for Specific Changes in a Collection

To watch for specific changes in a collection, pass a query that


matches change event fields to collection.watch():

for await (const change of plants.watch({


filter: {
operationType: "insert",
"fullDocument.type": "perennial",
},
})) {
// The change event will always represent a newly inserted perennial
const { documentKey, fullDocument } = change;
console.log(`new document: ${documentKey}`, fullDocument);
}

Aggregation Operations

Aggregation operations run all documents in a collection through a series of


stages called an aggregation pipeline. Aggregation allows you to filter and
transform documents, collect summary data about groups of related
documents, and other complex data operations.

Run an Aggregation Pipeline

To execute an aggregation pipeline, pass an array of aggregation stages


to collection.aggregate(). Aggregation operations return the result set of the
last stage in the pipeline.

The following snippet groups all documents in the plants collection by


their type value and aggregates a count of the number of each type:

const result = await plants.aggregate([


{
$group: {
_id: "$type",
total: { $sum: 1 },
},
},
{ $sort: { _id: 1 } },
]);
console.log(result);
HIDE OUTPUT
[
{ _id: "annual", total: 1 },
{ _id: "perennial", total: 5 },
]

Filter Documents

You can use the $match stage to filter documents according to standard
MongoDB query syntax.

{
"$match": {
"<Field Name>": <Query Expression>,
...
}
}

EXAMPLE

The following $match stage filters documents to include only those where
the type field has a value equal to "perennial":
const perennials = await plants.aggregate([
{ $match: { type: { $eq: "perennial" } } },
]);
console.log(perennials);
VIEW OUTPUT

Group Documents

You can use the $group stage to aggregate summary data for one or more
documents. MongoDB groups documents based on the expression defined in
the _id field of the $group stage. You can reference a specific document field
by prefixing the field name with a $.

{
"$group": {
"_id": <Group By Expression>,
"<Field Name>": <Aggregation Expression>,
...
}
}

EXAMPLE

The following $group stage arranges documents by the value of


their type field and calculates the number of plant documents that each
unique type value appears in.
const result = await plants.aggregate([
{
$group: {
_id: "$type",
numItems: { $sum: 1 },
},
},
{ $sort: { _id: 1 } },
]);
console.log(result);
VIEW OUTPUT

Paginate Documents

To paginate results, you can use range aggregation queries with


the $match, $sort, and $limit operators. To learn more about paginating
documents, refer to Using Range Queries in the MongoDB Server
documentation.

EXAMPLE

The following example paginates through a collection of documents in


ascending order.
// Paginates through list of plants
// in ascending order by plant name (A -> Z)
async function paginateCollectionAscending(
collection,
nPerPage,
startValue
){
const pipeline = [{ $sort: { name: 1 } }, { $limit: nPerPage }];
// If not starting from the beginning of the collection,
// only match documents greater than the previous greatest value.
if (startValue !== undefined) {
pipeline.unshift({
$match: {
name: { $gt: startValue },
},
});
}
const results = await collection.aggregate(pipeline);
return results;
}
// Number of results to show on each page
const resultsPerPage = 3;
const pageOneResults = await paginateCollectionAscending(
plants,
resultsPerPage
);
const pageTwoStartValue = pageOneResults[pageOneResults.length - 1].name;
const pageTwoResults = await paginateCollectionAscending(
plants,
resultsPerPage,
pageTwoStartValue
);
// ... can keep paginating for as many plants as there are in the collection

Project Document Fields

You can use the $project stage to include or omit specific fields from
documents or to calculate new fields using aggregation operators. Projections
work in two ways:

• Explicitly include fields with a value of 1. This has the side-effect of


implicitly excluding all unspecified fields.

• Implicitly exclude fields with a value of 0. This has the side-effect of


implicitly including all unspecified fields.

These two methods of projection are mutually exclusive: if you explicitly


include fields, you cannot explicitly exclude fields, and vice versa.

NOTE

The _id field is a special case: it is always included in every query unless
explicitly specified otherwise. For this reason, you can exclude the _id field
with a 0 value while simultaneously including other fields, like _partition,
with a 1. Only the special case of exclusion of the _id field allows both
exclusion and inclusion in one $project stage.
{
"$project": {
"<Field Name>": <0 | 1 | Expression>,
...
}
}

EXAMPLE

The following $project stage omits the _id field, includes the name field,
and creates a new field named storeNumber. The storeNumber is generated
using two aggregation operators:

1. $split separates the _partition value into two string segments


surrounding the space character. For example, the value "Store 42" split
in this way returns an array with two elements: "Store" and "42".

2. $arrayElemAt selects a specific element from an array based on the


second argument. In this case, the value 1 selects the second element
from the array generated by the $split operator since arrays index
from 0. For example, the value ["Store", "42"] passed to this operation
would return a value of "42".
const result = await plants.aggregate([
{
$project: {
_id: 0,
name: 1,
storeNumber: {
$arrayElemAt: [{ $split: ["$_partition", " "] }, 1],
},
},
},
]);
console.log(result);
VIEW OUTPUT

Add Fields to Documents


You can use the $addFields stage to add new fields with calculated values
using aggregation operators.

{ $addFields: { <newField>: <expression>, ... } }

NOTE

$addFields is similar to $project but does not allow you to include or omit
fields.

EXAMPLE

The following $addFields stage creates a new field


named storeNumber where the value is the output of two aggregate operators
that transform the value of the _partition field.
const result = await plants.aggregate([
{
$addFields: {
storeNumber: {
$arrayElemAt: [{ $split: ["$_partition", " "] }, 1],
},
},
},
]);
console.log(result);
VIEW OUTPUT

Unwind Array Values

You can use the $unwind stage to transform a single document containing an
array into multiple documents containing individual values from that array.
When you unwind an array field, MongoDB copies each document once for
each element of the array field but replaces the array value with the array
element in each copy.

{
$unwind: {
path: <Array Field Path>,
includeArrayIndex: <string>,
preserveNullAndEmptyArrays: <boolean>
}
}
EXAMPLE

The following example uses the $unwind stage for each


object's type and color combination. The aggregation pipeline has the
following steps:

1. Use $group stage with $addToSet to create new documents for


each type with a new field colors that contains an array of all the the
colors for that flower type that occur in the collection.

2. Use $unwind stage to create separate documents for each combination


of type and color.

3. Use $sort stage to sort the results in alphabetical order.


const result = await plants.aggregate([
{ $group: { _id: "$type", colors: { $addToSet: "$color" } } },
{ $unwind: { path: "$colors" } },
{ $sort: { _id: 1, colors: 1 } },
]);
console.log(result);

Manage Users - React Native SDK

Realm is now Atlas Device SDK – Learn More

When you use Atlas App Services to back your client app, you get access to
a user object. With this user object, you can:

• Create and delete users

• Log users in and out

• Create and update custom user data

• Read user metadata from social login providers

Create and Delete Users

For all authentication providers other than email/password authentication, App


Services automatically creates a user object the first time a user authenticates.
If a user authenticates via more than one method, you can link these user
identities to a single user object.

You can delete user objects. Deleting a user object deletes metadata attached
to the user object, but does not delete user-entered data from the backend.

TIP

Apple Account Deletion Requirements

Apple requires that applications listed through its App Store must give any
user who creates an account the option to delete the account. Whether you
use an authentication method where you must manually register a user, such
as email/password authentication, or one that that automatically creates a
user, such as Sign-In with Apple, you must implement user account
deletion by June 30, 2022.

Log Users In and Out

Use one or more authentication providers to log users in and out of your client
app. You can:

• Log users in with an existing social account, such as Apple, Facebook,


or Google.

• Create new user accounts with App Services email/password


management, or your own custom function or custom JWT user
management.

• Enable anonymous users to let users access your App Services App
without persisting user data.

When you have a logged-in user, SDK methods enable you to:

• Open a synced realm with the user's configuration object

• Run a backend function as the logged-in user


• Log the user out

• Change the active user in a multi-user application

• Remove a user from the device

On successful login, the React Native SDK caches credentials on the device.
You can bypass the login flow and access the cached user. Use this to open a
realm or call a function upon subsequent app opens.

User Sessions

App Services manages sessions with access tokens and refresh tokens. Client
SDKs supply the logic to manage tokens and provide them with requests.

Realm uses refresh tokens to automatically update a user's access token when
it expires. However, Realm does not automatically refresh the refresh token.
When the refresh token expires, the SDK can no longer get an updated access
token and the device cannot sync until the user logs in again.

For more information on managing user sessions and tokens, see User
Sessions in the App Services documentation.

Read and Update Custom User Data

You can associate custom data with a user object, such as a preferred
language or local timezone, and read it from your client application. A user
object has a customData property that you can use to access custom user
data.

To create and update custom user data, you must access your MongoDB data
source directly. App Services does not offer a SDK method to create or update
this custom user data; it's a read-only property.

Read User Metadata from Login Providers


Some authentication providers enable developers to access user metadata,
such as full name or email address. When you configure these metadata fields
on the App Services application, you can read this medata from your client
app. A user object has a profile property that you can use to access user
metadata.

Create and Delete Users - React Native SDK

Realm is now Atlas Device SDK – Learn More

Create a User

For most authentication methods, Realm automatically creates a user


account the first time a user authenticates. The only exception is
email/password authentication. When you use email/password authentication,
you must register and confirm a user before the user can authenticate to a
Realm application.

IMPORTANT

Google and Apple Account Deletion Requirements

Google and Apple require that applications listed through their respective App
Stores must give any user who creates an account the option to delete the
account. Whether you use an authentication method where you must manually
register a user, such as email/password authentication, or one that that
automatically creates a user, such as Sign-In with Apple, you must
implement user account deletion.

Delete a User

New in version 10.13.0.

Call the App.deleteUser() on a user object to delete the user's account from
your Realm application. This deletes the account from the server in addition to
clearing local data.
import React, {useState, useEffect} from 'react';
import {useApp, useUser} from '@realm/react';
function DeleteUser() {
const app = useApp();
const user = useUser();
async function deleteUser() {
// Delete the currently logged in user
await app.deleteUser(user);
}
// ...
}

To use your app in the future, the user must sign up for a new account. They
can use the same credentials (depending on the authentication provider), but
will not have the same User ID as their deleted account.

IMPORTANT

Deleting All User Data

Deleting a user only deletes the user object, which may contain associated
metadata from the associated auth provider. This does not delete custom user
data or other user data that your app stores in a linked collection or external
services.

Use the Authentication Trigger DELETE event to programmatically clean up


other data when you delete a user. For example, you can delete the user's data
from your custom user data collection or another service.

Google and Apple require that you disclose data retention and deletion
policies to your application customers and give them a way to request user
data deletion. If you collect additional user data, you must implement your own
methods or processes to delete that data.

Authenticate Users - React Native SDK

Realm is now Atlas Device SDK – Learn More


The SDK provides an API for authenticating users using any enabled
authentication provider. Instantiate a Realm.Credentials object and pass it
to Realm.App.logIn() to authenticate and obtain a Realm.User object.
The Realm.Credentials class exposes factory methods that correspond to
each of the authentication providers.

Prerequisites

Before you can authenticate a user, you must:

• Create an App Services App

• Enable one or more authentication providers in the App.

• Install the React Native SDK

Configure User Authentication in Client

Configure user authentication with


the @realm/react AppProvider and UserProvider components
and useApp() and useUser() hooks.

To set up user authentication:

1. Wrap all components you want to use with App Services in


an AppProvider component.

2. Inside of AppProvider, wrap all components that you want to have


access to an authenticated user with a UserProvider component.

3. In UserProvider, include a fallback prop with another component


that logs a user in. The app renders this component if there is no
authenticated user.

4. In the component passed to the UserProvider.fallback prop,


authenticate a user with Realm.App.logIn(), which you can access
with the useApp() hook.
Components wrapped by UserProvider only render if your app has an
authenticated user. These components can access the authenticated user with
the useUser() hook.

import React from 'react';


import {useApp, UserProvider, AppProvider} from '@realm/react';
import {Button} from 'react-native';
function AppWrapper() {
return (
<AppProvider id={APP_ID}>
{/* If there is no authenticated user,
the app mounts the `fallback` component.
Once the user successfully authenticates,
the app unmounts the component in the
`UserProvider.fallback` prop
(the `LogIn` component in this example). */}
<UserProvider fallback={LogIn}>
{/* Components with access to the user.
These components only mount
if there's an authenticated user.*/}
<RestOfApp />
</UserProvider>
</AppProvider>
);
}
function LogIn() {
const app = useApp();
// This example uses anonymous authentication.
// However, you can use any authentication provider
// to log a user in with this pattern.
async function logInUser() {
await app.logIn(Realm.Credentials.anonymous());
}
return (
<Button
title='Log In'
onPress={logInUser}
/>
);
}

Log In

Realm provides an API for authenticating users using any enabled


authentication provider. Instantiate a Credentials object and pass it to
the app.login() method to authenticate a user login and create
a User object.

User Sessions

The React Native SDK communicates with Atlas App Services to manage
sessions with access tokens and refresh tokens.

To learn more about session management, refer to User Sessions in the App
Services documentation.

Anonymous User

The Anonymous provider allows users to log in to your application with


temporary accounts that have no associated information.

To log in, create an anonymous credential and pass it to App.logIn():

// Create an anonymous credential


const credentials = Realm.Credentials.anonymous();
const user = await app.logIn(credentials);

Email/Password User

The email/password authentication provider allows users to log in to your


application with an email address and a password.

To log in, create an email/password credential with the user's email address
and password and pass it to App.logIn():

// Create an email/password credential


const credentials = Realm.Credentials.emailPassword(
"[email protected]",
"Pa55w0rd!"
);
const user = await app.logIn(credentials);

API Key User


The API key authentication provider allows server processes to access your
app directly or on behalf of a user.

To log in with an API key, create an API Key credential with a server or user
API key and pass it to App.logIn():

// Get the API key from the local environment


const apiKey = process.env?.appServicesApiKey;
if (!apiKey) {
throw new Error("Could not find a Server API Key.");
}
// Create an api key credential
const credentials = Realm.Credentials.apiKey(apiKey);
const user = await app.logIn(credentials);

Custom JWT User

The Custom JWT authentication provider allows you to handle user


authentication with any authentication system that returns a JSON web token.

To log in, create a Custom JWT credential with a JWT from the external system
and pass it to App.logIn():

// Create a custom jwt credential


const jwt = await authenticateWithExternalSystem();
const credentials = Realm.Credentials.jwt(jwt);
const user = await app.logIn(credentials);

Custom Function User

The Custom Function authentication provider allows you to handle user


authentication by running a function that receives a payload of arbitrary
information about a user.

To log in with the custom function provider, create a Custom Function


credential with a payload object and pass it to App.logIn():

// Create a custom function credential


const credentials = Realm.Credentials.function({
username: "ilovemongodb",
});
const user = await app.logIn(credentials);

Facebook User

The Facebook authentication provider allows you to authenticate users


through a Facebook app using their existing Facebook account.

To log a user in with their existing Facebook account, you must configure and
enable the Facebook authentication provider for your App Services App.

IMPORTANT

Do Not Store Facebook Profile Picture URLs

Facebook profile picture URLs include the user's access token to grant
permission to the image. To ensure security, do not store a URL that includes
a user's access token. Instead, access the URL directly from the user's
metadata fields when you need to fetch the image.

You can use the official Facebook SDK to handle the user authentication and
redirect flow from a client application. Once authenticated, the Facebook SDK
returns an access token that you can send to your React Native app and use to
finish logging the user in to your app.

// Get the access token from a client application using the Facebook SDK
const accessToken = getFacebookAccessToken();
// Log the user in to your app
const credentials = Realm.Credentials.facebook(accessToken);
app.logIn(credentials).then(user => {
console.log(`Logged in with id: ${user.id}`);
});

Google User

The Google authentication provider allows you to authenticate users with their
existing Google account.

To authenticate a Google user, you must configure the Google authentication


provider for your App Services App.
There is no official Sign in with Google integration with React Native. The
simplest approach to integrating Sign in With Google into your React Native
app with Realm authentication is to use a third-party library. The below
example uses the library React Native Google Sign In. You can also build your
own solution using Google Identity Services to handle the user authentication
and redirect flow from a client application.

Regardless of implementation, you must retrieve an ID token from the Google


Authorization server. Use that ID token to log into Realm.

// Get the Google OAuth 2.0 access token


const idToken = getGoogleAccessToken();
// Log the user in to your app
const credentials = Realm.Credentials.google({ idToken });
app.logIn(credentials).then((user) => {
console.log(`Logged in with id: ${user.id}`);
});

EXAMPLE

Authenticate with Google in React Native

This example uses the library React Native Google Sign In. In addition to the
React Native code, you must also set up additional configuration in your
project's ios and android directories to use Sign in with Google. Refer to the
package's documentation for iOS-specific and Android-
specific documentation.

SignInWithGoogleButton.jsx
import { useState } from "react";
import {
GoogleSignin,
GoogleSigninButton,
statusCodes,
} from "@react-native-google-signin/google-signin";
import Realm from "realm";
// Instantiate Realm app
const app = new Realm.App({
id: "<Your App ID>",
});
// Configure Google Auth
GoogleSignin.configure({
webClientId: "<Your Web Client ID>",
});
export default function GoogleSignInButton() {
const [signinInProgress, setSigninInProgress] = useState(false);
const signIn = async () => {
setSigninInProgress(true);
try {
// Sign into Google
await GoogleSignin.hasPlayServices();
const { idToken } = await GoogleSignin.signIn();
// use Google ID token to sign into Realm
const credential = Realm.Credentials.google({ idToken });
const user = await app.logIn(credential);
console.log("signed in as Realm user", user.id);
} catch (error) {
// handle errors
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
// user cancelled the login flow
} else if (error.code === statusCodes.IN_PROGRESS) {
// operation (e.g. sign in) is in progress already
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
} else {
// some other error happened
}
} finally {
setSigninInProgress(false);
}
};
// return Google Sign in button component
return (
<GoogleSigninButton
style={{ width: 192, height: 48 }}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
onPress={signIn}
disabled={signinInProgress}
/>
);
}

Apple User

The Apple authentication provider allows you to authenticate users through


Sign-in With Apple.
To authenticate an Apple user, you must configure the Apple authentication
provider for your App Services App.

You can use the official Sign in with Apple JS SDK to handle the user
authentication and redirect flow from a client application. Once authenticated,
the Apple JS SDK returns an ID token that you can send to your React Native
app and use to finish logging the user in to your app.

// Get the access token from a client application using the Apple JS SDK
const idToken = getAppleIdToken();
// Log the user in to your app
const credentials = Realm.Credentials.apple(idToken);
app.logIn(credentials).then(user => {
console.log(`Logged in with id: ${user.id}`);
});

TIP

If you get a Login failed error saying that the token contains an
invalid number of segments, verify that you're passing a UTF-8-encoded
string version of the JWT.

Offline Login

When your Realm application authenticates a user, it caches the user's


credentials. You can check for existing user credentials to bypass the login
flow and access the cached user. Use this to open a realm offline.

NOTE

Initial login requires a network connection

When a user signs up for your app, or logs in for the first time with an existing
account on a client, the client must have a network connection. Checking for
cached user credentials lets you open a realm offline, but only if the user has
previously logged in while online.
// Log user into your App Services App.
// On first login, the user must have a network connection.
const getUser = async () => {
// If the device has no cached user credentials, log in.
if (!app.currentUser) {
const credentials = Realm.Credentials.anonymous();
await app.logIn(credentials);
}
// If the app is offline, but credentials are
// cached, return existing user.
return app.currentUser!;
};

To learn how to use the cached user in the Sync Configuration and access a
realm while offline, read the Open a Synced Realm While Offline docs.

Get a User Access Token

When a user logs in, Atlas App Services creates an access token for the user
that grants them access to your App. The Realm SDK automatically manages
access tokens, refreshes them when they expire, and includes a valid access
token for the current user with each request. Realm does not automatically
refresh the refresh token. When the refresh token expires, the user must log in
again.

If you send requests outside of the SDK (for example, through the GraphQL
API) then you need to include the user's access token with each request, and
manually refresh the token when it expires.

You can access and refresh a logged in user's access token in the SDK from
their Realm.User object, as in the following example:

TypeScript
JavaScript
// Gets a valid user access token to authenticate requests
async function getValidAccessToken(user) {
// An already logged in user's access token might be stale. To
// guarantee that the token is valid, refresh it if necessary.
await user.refreshCustomData();
return user.accessToken;
}

Refresh Token Expiration


Refresh tokens expire after a set period of time. When the refresh token
expires, the access token can no longer be refreshed and the user must log in
again.

If the refresh token expires after the realm is open, the device will not be able
to sync until the user logs in again. Your sync error handler should implement
logic that catches a token expired error when attempting to sync, then redirect
users to a login flow.

For information on configuring refresh token expiration, refer to Manage User


Sessions in the App Services documentation.

Log a User Out

To log any user out, call the User.logOut() on their user instance.

WARNING

When a user logs out, you can no longer read or write data in any synced
realms that the user opened. As a result, any operation that has not yet
completed before the initiating user logs out cannot complete successfully
and will likely result in an error. Any data in a write operation that fails in this
way will be lost.
// Log out the current user
await app.currentUser?.logOut();

Custom User Data - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can read arbitrary data about your application users, known as custom
user data, directly within your React Native application. For example, you
might store a user's preferred language, date of birth, or local timezone.
Prerequisites

Before you can work with custom user data from your React Native app, you
must enable it in your App Services App. To learn more, refer to Enable
Custom User Data.

Read Custom User Data

WARNING

Custom Data May Be Stale

App Services does not dynamically update a user's custom data if the
underlying document changes. Instead, App Services fetches a new copy of
the data whenever a user refreshes their access token, such as when they log
in. This may mean that the custom data won't immediately reflect changes, e.g.
updates from an authentication Trigger. If the token is not refreshed, the SDK
waits 30 minutes and then refreshes it on the next call to the backend, so
custom user data could be stale for up to 30 minutes plus the time until the
next SDK call to the backend occurs.

If you have not recently updated your custom user data, use the user
object's customData field. The customData field is read only.

If you have updated your custom user data within the last 30 minutes,
use User.refreshCustomData().

import React, {useState, useEffect} from 'react';


import {useApp, useUser} from '@realm/react';
function ReadCustomUserData() {
const user = useUser();
const [customUserData, setCustomUserData] = useState();
// Access current custom user data with `user.customData`
function readCurrentCustomUserData() {
setCustomUserData(user.customData);
}
// Refresh custom user data with `user.refreshCustomData()`
async function refreshCustomUserData() {
const data = await user.refreshCustomData();
setCustomUserData(data);
}
// ...
}

Write Custom User Data

You can write to a user's custom user data with MongoDB Data Access.

Your write operations must include the User.id in the User ID Field you set
when configuring custom user data in the App backend. If you don't include
the user ID in the User ID Field, the data that you write will not be linked to the
user's custom user data.

import React, {useEffect} from 'react';


import {useApp, useUser} from '@realm/react';
function WriteCustomUserData() {
const user = useUser();
async function writeCustomUserData(favoriteColor: string) {
const customUserDataCollection = user
.mongoClient('mongodb-atlas')
.db('custom-user-data-database')
.collection('custom-user-data');
const filter = {
userId: user.id, // Query for the user object of the logged in user
};
const updateDoc = {
$set: {
// Set User ID if it's not already set
userId: user.id,
// Set the logged in user's favorite color
favoriteColor,
},
};
const options = {upsert: true};
await customUserDataCollection.updateOne(filter, updateDoc, options);
// Refresh custom user data once it's been updated on the server
const customUserData = await user.refreshCustomData();
console.log(customUserData);
}
// ...
}

NOTE
To modify the custom data field from a client or user function, write
permission to the collection in which custom data is stored must be
configured. If you prefer to restrict client write access to custom data from
your application, you can still modify the object from a system function.

Manage Email/Password Users - React Native SDK

Realm is now Atlas Device SDK – Learn More

If you have enabled the email/password authentication provider in your App


Services App, you can register a new account, confirm an email address, and
reset a user's password in React Native client code.

Prerequisites

Before you can manage email/password users from your React Native app,
you must enable email/password authentication in your App Services App
backend. To learn more about configuring email/password authentication,
refer to Email/Password Authentication in the App Services documentation.

Add AppProvider to Work with Email/Password Users

Wrap any components that need to manage email/password users with


the AppProvider. You can then access Realm.App.emailPasswordAuth to
manage email/password authentication with the useApp() hook.

TypeScript
JavaScript
import React from 'react';
import {useApp, UserProvider, AppProvider} from '@realm/react';
import Realm from 'realm';
function AppWrapper() {
return (
<View>
<AppProvider id={APP_ID}>
<UserProvider fallback={<RegisterUser />}>
{/* ...Other components in app that require authentication */}
</UserProvider>
</AppProvider>
</View>
);
}
function RegisterUser() {
const app = useApp();
async function register(email, password) {
// Register new email/password user
await app.emailPasswordAuth.registerUser({email, password});
// Log in the email/password user
await app.logIn(Realm.Credentials.emailPassword(email, password));
}
// ...
}

Register a New User Account

To register a new email/password user, pass the user's email address and
desired password to EmailPasswordAuth.registerUser(). The email address
must not be associated with another email/password user and the password
must be between 6 and 128 characters.
After registration, you must confirm a new user's email address before they
can log in to your app.

await app.emailPasswordAuth.registerUser({
email: "[email protected]",
password: "Pa55w0rd!",
});

Confirm a New User's Email Address

New users must confirm that they own their email address before they can log
in to your app unless the provider is configured to automatically confirm new
users. The confirmation process starts when you register a user and ends
when you confirm them from your client code.

You need a valid token and tokenId for a registered user in order to confirm
them and allow them to log in. These values are available in different places
depending on the provider configuration:

• If the provider is set to send a confirmation email,


the token and tokenId values are included as query parameters in
the Email Confirmation URL.

• If the provider is set to run a confirmation function,


the token and tokenId values are passed to the function as
arguments.

To confirm a registered user, pass a


valid token and tokenId to EmailPasswordAuth.confirmUser().

const token = "someToken";


const tokenId = "someTokenId";
try {
await app.emailPasswordAuth.confirmUser({ token, tokenId });
// User email address confirmed.
console.log("Successfully confirmed user.");
} catch (err) {
console.log(`User confirmation failed: ${err}`);
}

Retry User Confirmation Methods

The SDK provides methods to resend user confirmation emails or retry custom
confirmation methods.

Resend a Confirmation Email

If the provider is configured to send a confirmation email, Atlas App Services


automatically sends a confirmation email when a user registers. The email
contains a link to the configured Email Confirmation URL with a token that is
valid for 30 minutes. If a user does not follow the link and confirm within that
period, they must request a new confirmation email.

To send a new confirmation email to a user, pass their email address


to EmailPasswordAuth.resendConfirmationEmail().

const email = "[email protected]";


await app.emailPasswordAuth.resendConfirmation({ email });

Retry a User Confirmation Function

To re-run your custom confirmation function, call


the retryCustomConfirmation() method with the user's email address:

const email = "[email protected]";


await app.emailPasswordAuth.retryCustomConfirmation({ email });
Reset a User's Password

Resetting a user's password is a multi-step process.

1. In your client app, you provide a UI for the user to reset their password.
Your App Services App can then send an email or run a custom function
to confirm the user's identity.

2. After confirming the user's identity, you can complete the password
reset request.

3. After the password reset is complete, the user can log in using the new
password.

For more information about how to set your preferred password reset method,
refer to the App Services Email/Password Authentication documentation.

Send a Password Reset Email

To send password reset emails to confirm the user's identity, you must
configure your App to send a password reset email.

To begin the password reset process,


call EmailPasswordAuth.sendResetPasswordEmail() with the user's email. The
email contains a link to the configured Password Reset URL. The user must
visit this URL within 30 minutes to confirm the reset.

const email = "[email protected]";


await app.emailPasswordAuth.sendResetPasswordEmail({ email });

After the user has visited the URL from the password reset email,
call EmailPasswordAuth.resetPassword() with the user's email, the new
password, and the token and tokenId provided in the unique URL.

await app.emailPasswordAuth.resetPassword({
password: "newPassw0rd",
token,
tokenId,
});

If the user does not visit the URL from the password reset email within 30
minutes, the token and tokenId expire. You must begin the password reset
process again.

Call a Password Reset Function

When you configure your app to run a password reset function, you define the
function that should run when you
call EmailPasswordAuth.callResetPasswordFunction().

This function can take a username, a password, and any number of additional
arguments. You can use these arguments to specify details like security
question answers or other challenges that the user should pass to
successfully complete a password reset.

You might prefer to use a custom password reset function when you want to
define your own password reset flows. For example, you might send a custom
password reset email from a specific domain. Or you might use a service other
than email to confirm the user's identity.

On the App Services side, you define the custom password reset function that
runs when you call this method. That function can return one of three possible
statuses:

• fail

• pending

• success
A fail status is treated as an error by the SDK. The
SDK callResetPasswordFunction() does not take return values, so it does
not return a pending or success status to the client.

Server-Side Pending Case

Your App Services password reset function may return pending if you want
the user to take some additional step to confirm their identity. However, that
return value is not passed to the SDK's callResetPasswordFunction(), so
your client app must implement its own logic to handle a pending status.

const email = "[email protected]";


// The new password to use
const password = "newPassw0rd";
// Additional arguments for the reset function
const args = [];
await app.emailPasswordAuth.callResetPasswordFunction(
{ email, password },
args
);

Your server-side function might send an email using a custom email provider.
Or you may use SMS to confirm the user's identity via text message.

You have access to a token and tokenId in the App Services password reset
function context. If you pass this information from your App Services
password reset function, you can pass these values back to your app using
platform-specific deep linking or universal links. Then, your client application
can call EmailPasswordAuth.resetPassword() to complete the password reset
flow.

await app.emailPasswordAuth.resetPassword({
password: "newPassw0rd",
token,
tokenId,
});

Server-Side Success Case

If your App Services password reset function does additional validation within
the function, or if you have validated the user's identity prior to attempting to
reset the password, you may configure the App Services function to
return success. However, that return value is not passed to the
SDK's callResetPasswordFunction(), so your client app must implement
its own logic to handle a success status.

Calling the function in this example performs the entire password reset
process.

await app.emailPasswordAuth.resetPassword({
password: "newPassw0rd",
token,
tokenId,
});

Multi-User Applications - React Native SDK

Realm is now Atlas Device SDK – Learn More

The Realm SDK allows multiple users to be logged in to an app simultaneously


on a given device. Atlas App Services client applications run in the context of
a single active user even if multiple users are logged in simultaneously. You
can quickly switch between authenticated users without requiring them to log
in again.

IMPORTANT
Any logged-in user may become the active user without re-
authenticating. Depending on your app, this may be a security vulnerability.
For example, a user on a shared device may switch to a coworker's logged in
account without providing their credentials or requiring their explicit
permission. If your application requires stricter authentication, avoid switching
between users and prefer to explicitly log the active user out before
authenticating another user.

User Account States

When a user first logs in through a Realm SDK on a device, the SDK saves the
user's information and keeps track of the user's state on the device. The user's
data remains on the device, even if they log out, unless you actively remove
the user.

The following states describe an on-device user at any given time:

• Authenticated: any user that has logged in on the device and has not
logged out or had its session revoked.

o Active: a single authenticated user that is currently using the app


on a given device. The SDK associates this user with outgoing
requests and App Services evaluates data access permissions
and runs functions in this user's context. See active user for more
information.

o Inactive: all authenticated users that are not the current active
user. You can switch the active user to a currently inactive user at
any time.

• Logged Out: any user that authenticated on the device but has since
logged out or had their session revoked.

The following diagram shows how users within an App Services client app
transition between states when certain events occur:
Before You Begin

If you're using @realm/react, you must wrap any components that you want
to manage users with in the AppProvider component. Components wrapped
with an AppProvider can use the useApp() hook to access
the Realm.App client.

For more information on using the AppProvider component


and useApp() hook, refer to Connect to an Atlas App Services App.

Add a New User to the Device

The Realm SDK automatically adds users to a device when they log in for the
first time on that device. When a user logs in, they immediately become the
application's active user.

// Log in as Joe
const joeCredentials = Realm.Credentials.emailPassword("[email protected]", "passw0rd")
const joe = await app.logIn(joeCredentials);
// The active user is now Joe
assert(joe.id === app.currentUser.id);
// Log in as Emma
const emmaCredentials = Realm.Credentials.emailPassword("[email protected]", "pa55word")
const emma = await app.logIn(emmaCredentials);
// The active user is now Emma, but Joe is still logged in
assert(emma.id === app.currentUser.id);

List All Users on the Device

You can access a list of all user accounts on the device


with Realm.App.allUsers. This property includes an array all users that have
logged in to the client app on a device.

// Get a list of all Users


app.allUsers.forEach(user => {
console.log(`User with id ${user.id} is ${user.isLoggedIn ? "logged in" : "logged out"}`);
});

Remove a User from the Device

You can remove all information about a user from the device and automatically
log the user out with Realm.App.removeUser(). This method does not delete
the user from the backend App.

// Remove the current user from the device


const user = app.currentUser;
await app.removeUser(user);
// The user is no longer the active user
if(app.currentUser) {
// The active user is now the logged in user (if there still is one) that was
// most recently active
assert(user.id !== app.currentUser.id)
}
// The user is no longer on the device
assert(app.allUsers.find(({ id }) => id === user.id) === undefined);

Change the Active User

You can quickly switch an app's active user to another logged-in user at any
time with Realm.App.switchUser().

// Get some logged-in users


const authenticatedUsers = app.allUsers.filter(user => user.isLoggedIn);
const user1 = authenticatedUsers[0];
const user2 = authenticatedUsers[1];
// Switch to user1
app.switchUser(user1);
// The active user is now user1
assert(app.currentUser.id === user1.id);
// Switch to user2
app.switchUser(user2);
// The active user is now user2
assert(app.currentUser.id === user2.id);

Link User Identities - React Native SDK

Realm is now Atlas Device SDK – Learn More

Realm provides many authentication providers to log users into your app.
Each provider creates a unique user identity. Realm lets you merge multiple
credentials into one user identity.

You can link identities using User.linkCredentials(). This links authentication


providers to a logged-in User object.

import React, {useEffect, useState} from 'react';


import {AppProvider, UserProvider, useApp, useUser} from '@realm/react';
import Realm from 'realm';
import {View, Button, Text, TextInput} from 'react-native';
function AppWrapper() {
return (
<View>
<AppProvider id={APP_ID}>
<UserProvider fallback={<AnonymousLogIn />}>
{/* ...Rest of app */}
<SignUpUser />
</UserProvider>
</AppProvider>
</View>
);
}
// Log in an anonymous user when the app opens
// if not already logged in current user.
function AnonymousLogIn() {
const app = useApp();
useEffect(() => {
app.logIn(Realm.Credentials.anonymous());
}, []);
return null;
}
// Link user credentials. The component contains a form
// where the user can add and link credentials.
function SignUpUser() {
const app = useApp();
const user = useUser();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
// Link email/password credentials to anonymous user
// when creating and logging in email/password user.
const registerAndLinkIdentities = async () => {
try {
await app.emailPasswordAuth.registerUser({email, password});
const credentials = Realm.Credentials.emailPassword(email, password);
await user.linkCredentials(credentials);
} catch (err) {
// Add error handling logic here
}
};
return (
<View>
<Text>Log In User</Text>
<View>
<Text>Email Address:</Text>
<TextInput
value={email}
onChangeText={setEmail}
/>
</View>
<View>
<Text>Password</Text>
<TextInput
value={password}
onChangeText={setPassword}
/>
</View>
<Button
onPress={registerAndLinkIdentities}
title='Link Credentials'
/>
</View>
);
}
Create & Manage User API Keys - React Native SDK

Realm is now Atlas Device SDK – Learn More

User API keys allow devices or services to communicate with App Services on
behalf of a user without sharing that users credentials. User API keys can be
revoked at any time by the authenticated user. User API keys do not expire on
their own.

You can manage a user API key with the ApiKeyAuth client accessed with an
authenticated user's User.apiKeys property.

If you are using @realm/react, you can access a user's ApiKeyAuth client
with the useUser() hook in a component wrapped by UserProvider.

import React, {useEffect} from 'react';


import {useUser} from '@realm/react';
function UserApiKeys() {
const user = useUser();
async function createUserApiKey() {
const apiKey = await user?.apiKeys.create('mySecretKey');
// ...Do something with API key like save it
// or share it with external service that authenticates
// on user's behalf.
}
// ...
}

Create a User API Key

To create a new user API key, pass a name that's unique among all of the
user's API keys to ApiKeyAuth.create().

The SDK returns the value of the user API key when you create it. Make sure to
store the key value securely so that you can use it to log in. If you lose or do
not store the key value there is no way to recover it. You will need to create a
new user API key.

You cannot create a user API key for a server API key or an anonymous user.
const key = await user.apiKeys.create("apiKeyName");

Look up a User API Key

To get an array that lists all of a user's API keys, call ApiKeyAuth.fetchAll().

To find a specific API key, pass the key's _id to ApiKeyAuth.fetch().

// List all of a user's keys


const keys = await user.apiKeys.fetchAll();
// Get a specific key by its ID
const key = await user.apiKeys.fetch("5eb5931548d79bc784adf46e");

Enable or Disable an API Key

To enable or disable a user API key, pass the


key's _id to ApiKeyAuth.enable() or ApiKeyAuth.disable(). When a key is
disabled, it cannot be used to log in on behalf of the user.

const apiKeys = await user.apiKeys.fetchAll();


const keyId = apiKeys[0]["_id"];
// Enable the User API Key
await user.apiKey.enable(keyId);
// Disable the User API Key
await user.apiKey.disable(keyId);

Delete an API Key

To permanently delete a user API, pass the key's _id to ApiKeyAuth.delete().


Deleted keys cannot be recovered.

const apiKeys = await user.apiKeys.fetchAll();


const keyId = apiKeys[0]["_id"];
// Delete the User API Key
await user.apiKey.delete(keyId);

Sync Data Between Devices - React Native SDK

Realm is now Atlas Device SDK – Learn More


Atlas Device Sync automatically synchronizes data between client applications
and an Atlas App Services backend. When a client device is online, Sync
asynchronously synchronizes data in a background thread between the device
and your backend App.

TIP

See also:

Configure and Enable Atlas Device Sync

Flexible Sync

When you select Flexible Sync for your backend App configuration, your client
implementation must include subscriptions to queries on queryable fields.
Flexible Sync works by synchronizing data that matches query subscriptions
you maintain in the client application.

A subscription set contains a set of queries. Realm Flexible Sync returns


documents matching those queries, where the user has the
appropriate permissions to read and/or read and write the documents. If
documents match the query, but the client does not have the permission to
read or write them, they do not sync to the client application.

You can form queries using Realm Query Language.

NOTE

Flexible Sync does not support all the query operators available in Realm
Query Language and the SDK's query engine. See Flexible Sync RQL
Limitations for details.

Subscription sets are based on a specific type of Realm object. You might
have multiple subscriptions if you have many types of Realm objects.
To use Flexible Sync in your client application, open a synced realm with a
Flexible Sync configuration. Then, manage subscriptions to determine which
documents to sync.

TIP

See also:

• Learn how to configure and open a realm using Flexible Sync.

• Learn how to Manage Flexible Sync Subscriptions

Group Updates for Improved Performance

Every write transaction for a subscription set has a performance cost. If you
need to make multiple updates to a Realm object during a session, consider
keeping edited objects in memory until all changes are complete. This
improves sync performance by only writing the complete and updated object
to your realm instead of every change.

TIP

Device Sync supports two Sync Modes: Flexible Sync, and the older Partition-
Based Sync. If your App Services backend uses Partition-Based Sync, refer
to Partition-Based Sync - React Native SDK.

We recommend new apps use Flexible Sync.

Configure a Synced Realm - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can configure a realm to automatically synchronize data between many


devices that each have their own local copy of the data.
For more information about synced realms, including directions on how to set
up sync in an App Services App, refer to Atlas Device Sync Overview.

For more information about different realm configurations, refer to Configure a


Realm.

Prerequisites

Before you configure a realm with Flexible Sync in a React Native application:

1. Enable Flexible Sync on the backend. You must configure Flexible Sync
in the backend before you can use it with your client application.

2. Initialize the App client.

3. Authenticate a user in your client project.

Configure a Synced Realm

Configure a synced realm using the providers from @realm/react.

By default, Realm syncs all data from the server before returning anything. If
you want to sync data in the background, read Configure a Synced Realm
While Offline.

To configure a synced realm:

1. Import providers from @realm/react.

2. Configure AppProvider.

3. Configure UserProvider and nest it within AppProvider.

4. Configure RealmProvider for sync and nest it within UserProvider.


You must set up a sync subscription. The example below uses an initial
subscription, but you can also set up subscriptions
in RealmProvider child components.

This is how you nest providers:


import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
schema={[YourObjectModel]}
sync={{
flexible: true,
initialSubscriptions: {
update(subs, realm) {
subs.add(realm.objects(YourObjectModel));
},
},
}}>
<RestOfApp />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

NOTE

Partition-Based Sync

This page covers Flexible Sync realms. Flexible Sync is the preferred mode for
new apps that use Atlas Device Sync. For information about realms using the
older Partition-Based Sync, refer to Partition-Based Sync.
AppProvider

The @realm/react AppProvider gives you access to an instance of your App


Services App.

To set up your App client, pass the App ID string to the id prop
of AppProvider. Wrap any components that need to access the App with
the AppProvider.

import React from 'react';


import {AppProvider} from '@realm/react';
function AppWrapper() {
return (
<View>
<AppProvider id={APP_ID}>
<MyApp />
</AppProvider>
</View>
);
}

You can find more information about AppProvider on the Connect To Atlas
App Services page.

UserProvider

UserProvider gives you access to a Realm user. A UserProvider is required


for an app to use the hooks.

First, you need to configure user authentication


with AppProvider and UserProvider. Then, work with authentication using
the useApp() and useUser() hooks.
To set up user authentication:

1. Wrap all components you want to use with App Services in


an AppProvider.

2. Inside of AppProvider, wrap all components that need access to an


authenticated user with a UserProvider.

3. In UserProvider, include a fallback prop with another component


that logs a user in. The app renders this component if there is no
authenticated user.

4. In the component passed to the UserProvider.fallback prop,


authenticate a user with Realm.App.logIn(), which you can access
with the useApp() hook.

Components wrapped by UserProvider only render if your app has an


authenticated user. These components can access the authenticated user with
the useUser() hook.

import React from 'react';


import {useApp, UserProvider, AppProvider} from '@realm/react';
import {Button} from 'react-native';
function AppWrapper() {
return (
<AppProvider id={APP_ID}>
{/* If there is no authenticated user,
the app mounts the `fallback` component.
Once the user successfully authenticates,
the app unmounts the component in the
`UserProvider.fallback` prop
(the `LogIn` component in this example). */}
<UserProvider fallback={LogIn}>
{/* Components with access to the user.
These components only mount
if there's an authenticated user.*/}
<RestOfApp />
</UserProvider>
</AppProvider>
);
}
function LogIn() {
const app = useApp();
// This example uses anonymous authentication.
// However, you can use any authentication provider
// to log a user in with this pattern.
async function logInUser() {
await app.logIn(Realm.Credentials.anonymous());
}
return (
<Button
title='Log In'
onPress={logInUser}
/>
);
}

You can find more information about UserProvider on the Authenticate


Users page.

RealmProvider

RealmProvider is a wrapper that exposes a realm to its child components. You


configure your realm by passing props to RealmProvider.
When RealmProvider is rendered, it opens the realm. This means that the
provider renders successfully or its child components can't access the realm.

To configure a synced realm:

1. Import providers from @realm/react.

2. Configure AppProvider.

3. Configure UserProvider and nest it within AppProvider.

4. Pass your object models to RealmProvider's schema prop.

5. Create a FlexibleSyncConfiguration object.

6. Pass your SyncConfiguration object to the sync prop or add the


object in-line.

7. Set up initial subscriptions or create a new subscription


in RealmProvider child components.

8. Add other Configuration object properties as props


to RealmProvider to further configure your realm.
import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>

<RealmProvider
schema={[YourObjectModel]}
sync={{
flexible: true,
initialSubscriptions: {
update(subs, realm) {
subs.add(realm.objects(YourObjectModel));
},
},
}}>
<RestOfApp />
</RealmProvider>

</UserProvider>
</AppProvider>
);
}

For more information about configuring and using RealmProvider, check out
the Configure a Realm page.

Configuration Options

You can configure RealmProvider by setting props that match the properties
of a Configuration object. You can also set fallback and realmRef props.

• realmRef
Used with useRef to expose the configured realm to processes outside
of RealmProvider. This can be useful for things like a client reset fallback.

• fallback
Rendered while waiting for the realm to open. Local realms usually open fast
enough that the fallback prop isn't needed.

Open Synced Realm at Specific Path

New in version [email protected] .

Using AppConfiguration.baseFilePath, and Realm.BaseConfiguration.path, you


can control where Realm and metadata files are stored on client devices.
To do so, set AppProvider.baseFilePath. If baseFilePath is not set, the
current work directory is used. You can also
set RealmProvider.sync.path for more control.

TypeScript
JavaScript
import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperSync({customBaseFilePath}) {
return (

<AppProvider id={APP_ID} baseFilePath={customBaseFilePath}>

<UserProvider fallback={LogIn}>
<RealmProvider

path={customRealmPath}

schema={[Profile]}
sync={{
flexible: true,
}}>
<RestOfApp />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

If baseFilePath is set, metadata is always stored


in <baseFilePath>/mongodb-realm/. If baseFilePath isn't set, then
metadata is stored in <Realm.defaultPath>/mongodb-realm.

Where, exactly, your Realm file is stored can vary depending on how you
set Realm.BaseConfiguration.path:
• Realm.Configuration.path is not set and baseFilePath is set. Your
Realm file is stored at baseFilePath.

• Realm.Configuation.path is set to a relative path. Your Realm file is


stored relative to baseFilePath.

• Realm.Configuration.path is an absolute path. Your Realm file is


stored at Realm.Configuration.path.

Access a Synced Realm While Offline

The following subsections show how to use background synchronization to


access a realm while offline. To do this, use a cached user and
an OpenRealmBehaviorConfiguration object.

Within RealmProvider's sync configuration, set the


optional newRealmFileBehavior and existingRealmFileBehavior fields
to your OpenRealmBehaviorConfiguration object to enable background
synchronization.

You can open a realm immediately with background sync or after a timeout.

NOTE

Initial login requires a network connection

When a user signs up for your app, or logs in for the first time with an existing
account on a client, the client must have a network connection. Checking for
cached user credentials lets you open a realm offline, but only if the user has
previously logged in while online.

Access Immediately with Background Sync

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.

TypeScript
JavaScript
import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperOfflineSync() {
const realmAccessBehavior = {
type: 'openImmediately',
};
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
schema={[Profile]}
sync={{
flexible: true,
newRealmFileBehavior: realmAccessBehavior,
existingRealmFileBehavior: realmAccessBehavior,
}}>
<RestOfApp />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

Access After Timeout with Background Sync


If you want to sync data but you're in an environment where it's uncertain if the
user has an Internet connection, specify a timeOut. This automatically opens
the realm when either:

• the timeout period elapses.

• the realm has completely downloaded.

If the realm doesn't finish downloading before the timeout, the initial Sync
continues in the background.

TypeScript
JavaScript
import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperTimeoutSync() {
const realmAccessBehavior = {
type: 'downloadBeforeOpen',
timeOutBehavior: 'openLocalRealm',
timeOut: 1000,
};
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
schema={[Profile]}
sync={{
flexible: true,
newRealmFileBehavior: realmAccessBehavior,
existingRealmFileBehavior: realmAccessBehavior,
}}>
<RestOfApp />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

@realm/react Providers and Hooks

@realm/react has providers and hooks that simplify working with your
synced realm and its data.

AppProvider

Provider/Hook Description Example

AppProvider React component that provides an See AppProvider


App Services App instance for the
Sync hooks. An AppProvider is
required for a client to use Sync
hooks.
useApp Accesses the current App Services const app = useApp();
App instance from
the AppProvider context.

useAuth Provides methods and state for const auth = useAuth();


authenticating with an App
Services App.
useEmailPasswordAuthProvides methods and state for const auth = useEmailPasswordA
authenticating with an App
Services App using email and
password authentication. It also
contains utility methods, like
resetting a password and
confirming a user.

UserProvider
Provider/Hook Description Example

UserProvider React component that provides a Realm user for the See UserProvider
Sync hooks. A UserProvider is required for an app to use
Sync hooks.
useUser Accesses the currently-authenticated Realm user from const user = useUse
the UserProvider context. The user is stored as React
state and will trigger a re-render whenever it changes.

RealmProvider

Provider/Hook Description Example

RealmProviderA wrapper that exposes a realm to its child See RealmProvider


components, which have access to hooks
that let you read, write, and update data.
useRealm Returns the instance of the Realm opened const realm = useRealm();
by the RealmProvider.
useObject Returns an object (Realm.Object<T>) from const myTask = useObject(Task,
a given type and value of primary key.
Updates on any changes to the returned
object. Returns null if the object either
doesn't exists or has been deleted.
useQuery Returns a collection of objects const tasks = useQuery(Task);
(Realm.Results<T & Realm.Object T>)
from a given type. Updates on any changes
to any object in the collection. Returns an
empty array if the collection is empty.

Manage Sync Subscriptions - React Native SDK

Realm is now Atlas Device SDK – Learn More


Flexible Sync uses subscriptions and permissions to determine what data to
sync with your App. You must have at least one subscription before you can
read from or write to a realm with Flexible Sync enabled.
The @realm/react library streamlines permissions and queries for sync
subscriptions.

You can add, update, and remove query subscriptions to control what data
syncs to the client device. In the Realm.js v12.0.0 and later, you can subscribe
to queries instead of or in addition to manually managing subscriptions.

You can't create subscriptions for Data Ingest and asymmetric


objects because they only send data to your App Services backend.

IMPORTANT

Flexible Sync Query Limitations

Flexible Sync subscriptions only support a subset of the RQL query operators.
Refer to the Flexible Sync RQL Limitations documentation for information on
which operators are not supported.

Prerequisites

You need to meet the following requirements before you can use Atlas Device
Sync with the React Native SDK:

• A non-sharded Atlas cluster running MongoDB 5.0 or later.

• Realm.js v10.12.0 or later.

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

1. Configure Flexible Sync on the backend

2. Initialize the app client

3. Authenticate a user in the client


4. Configure a synced Realm

Align Subscriptions with Backend App

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.

Subscribe to Queries

New in version [email protected] .

[email protected] adds experimental APIs that subscribe to and unsubscribe


from a query's results. These APIs abstract away the details of manually
adding and removing subscriptions.

For all subscriptions, you need an authenticated user and a Flexible Sync
realm.

Subscribe to a Query

We recommend that you name your subscriptions. This makes finding and
managing your subscriptions easier. Subscription names must be unique.
Trying to add a subscription with the same name as an existing subscription
overwrites the existing subscription.

To subscribe to a query:
1. Query for the objects that you want to read and write.

2. Call subscribe() on the query results to create a sync subscription for


objects matching the query.

3. Pass a SubscriptionOptions object that contains the name property


to subscribe().
import React, {useEffect, useState} from 'react';
import {useRealm, useQuery} from '@realm/react';
import {View, Text, FlatList} from 'react-native';
import {Bird} from './models/Bird';
import {Subscription} from 'realm/dist/bundle';
export const BasicSubscription = () => {
const realm = useRealm();
// Get all local birds that have not been seen yet.
const seenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == true'),
);
const [seenBirdsSubscription, setSeenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
// Create an async function so that we can `await` the
// promise from `.subscribe()`.
const createSubscription = async () => {
await seenBirds.subscribe({
name: 'Birds I have seen',
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('Birds I have seen');
// ... and set it to a stateful variable or manage it in `useEffect`.
setSeenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

Most of the time, you should give your subscriptions a name. If you don't, the
name is set to null.
If you use filtered() on an unnamed query subscription, the subscription
identifier is based on the filtered query. This means that every time your
query string changes, subscribe() will create a new subscription.

API Reference

• subscribe()

• SubscriptionOptions

• null

Wait for a Query Subscription to Sync

When you subscribe to a query's results, the results do not contain objects
until synced data is downloaded. When you do need to wait for synced objects
to finish downloading, configure the waitForSync option. You can specify
different behavior for your subscriptions and how they handle writing for
downloads.

This example uses the FirstTime option, which is the default behavior. A
subscription with FirstTime behavior only waits for sync to finish when a
subscription is first created.

import React, {useEffect, useState} from 'react';


import {BSON, WaitForSync} from 'realm';
import {useRealm, useQuery} from '@realm/react';
import {View, Text, Button, TextInput, FlatList} from 'react-native';
import {Bird} from './models/Bird';
import {Subscription} from 'realm/dist/bundle';
export const WaitFirstTime = () => {
const realm = useRealm();
const [birdName, setBirdName] = useState('Change me!');
// Get local birds that have been marked as "haveSeen".
const seenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == true'),
);
const [seenBirdsSubscription, setSeenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
const createSubscription = async () => {
// Only wait for sync to finish on the initial sync.
await seenBirds.subscribe({
behavior: WaitForSync.FirstTime,
name: 'First time sync only',
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('First time sync only');
// ... and set it to a stateful variable or manage it in `useEffect`.
setSeenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

The other supported WaitForSync options are:

• Always: Wait to download matching objects every time your app


launches. The app must have an internet connection at every launch.

• Never: Never wait to download matching objects. The app needs an


internet connection for the user to authenticate the first time the app
launches, but can open offline on subsequent launches using cached
credentials.

You can optionally specify a timeout value to limit how long the sync
download runs:

export const AlwaysWait = () => {


const realm = useRealm();
// Get all local birds that have not been seen yet.
const unSeenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == false'),
);
const [unSeenBirdsSubscription, setUnseenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
const createSubscription = async () => {
// Add subscription with timeout.
// If timeout expires before sync is completed, currently-downloaded
// objects are returned and sync download continues in the background.
await unSeenBirds.subscribe({
behavior: WaitForSync.Always,
name: 'Always wait',
timeout: 500,
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('Always wait');
// ... and set it to a stateful variable or manage it in `useEffect`.
setUnseenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

API Reference

• waitForSync

Unsubscribe from a Query

Subscriptions persist across user sessions unless you unsubscribe from


them. You can unsubscribe from a query's results using unsubscribe().

This removes the subscription from the list of active subscriptions, similar
to manually removing a subscription.

A results list may still contain objects after calling unsubscribe() if another
subscription exists that contains overlapping objects.

When you call unsubscribe(), the associated subscription is removed.


Subscriptions are removed by name. If they don't have a
name, unsubscribe() removes any queries that exactly match the one you
call unsubscribe() on.

The unsubscribe() method returns before objects matching the removed


subscription are deleted from the realm. Sync continues in the background
based on the new set of subscriptions.

import React, {useEffect, useState} from 'react';


import {useRealm, useQuery} from '@realm/react';
import {View, Text, Button} from 'react-native';
import {Bird} from './models/Bird';
import {Subscription} from 'realm/dist/bundle';
export const Unsubscribe = () => {
const realm = useRealm();
const birds = useQuery(Bird);
const unsubscribeFromQuery = () => {
birds.unsubscribe();
};
return (
<View>
<Button
title="Unsubscribe"
onPress={() => {
unsubscribeFromQuery();
}}
/>
</View>
);
};

API Reference

• unsubscribe()

Manually Manage Subscriptions

You can use the Subscriptions API to manually manage a set of subscriptions
to specific queries on queryable fields.

You can use the Realm.subscriptions API 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() hook gives you access
to the currently-opened realm.

You can do the following with your subscriptions:

• Add subscriptions

• Configure a realm with initial subscriptions


• Get a list of all subscriptions

• Check the status of subscriptions

• Remove 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.

When you create a subscription, Realm looks for data matching a query on a
specific object type. You can have subscriptions on several different object
types. You can also have multiple queries on the same object type.

IMPORTANT

Object Links

You must add both an object and its linked object to the subscription set to
see a linked object.

If your subscription results contain an object with a property that links to an


object not contained in the results, the link appears to be null. There is no way
to distinguish whether that property's value is legitimately null, or whether the
object it links to exists but is out of view of the query subscription.

Access All Subscriptions

Within a RealmProvider configured for Flexible Sync, you can access


a SubscriptionSet. A SubscriptionSet is a collection of all subscriptions for
your app.

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>}
/>
);
}

API Reference

• SubscriptionSet

Add a Subscription

In the following example, completed and progressMinutes have been set as


queryable fields in an App Services App. In the client code, we create filtered
queries and then subscribe to their results:

• Completed tasks

• Completed tasks that have taken over 120 progressMinutes

Note that 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>}
/>
);
}

Configure a Realm with Initial Subscriptions

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 a RealmProvider's sync
configuration.

You can't use the @realm/react library


hooks useQuery and useObject when setting initial subscriptions. Instead,
use the Realm.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>}
/>
);
}

By default, initial subscriptions are only created the first time a realm is
opened. If your app needs to rerun this initial subscription every time the app
starts, you can set rerunOnOpen to true. You might need to do this to rerun
dynamic time ranges or other queries that require a recomputation of static
variables for the subscription.

API Reference

• FlexibleSyncConfiguration

Check the Status of Subscriptions

You can check the subscription state to see if the server has acknowledged
the subscription 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>
);
}

New in version [email protected] .

Realm.js v12.0.0 added the SubscriptionSetState enum that you can use to get
the status of a subscription.

Subscription State "Complete"

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.

Update Subscriptions with a New Query

You can update a named subscription with a new query. To update a


subscription's query, pass the new query and a subscription option with the
name of the subscription that you want to update to
the MutableSubscriptionSet.add() method. Like adding a new
subscription, you must update a subscription within a transaction by
calling subscriptions.update().

The following example, redefines long-running tasks as any tasks that take
more than 180 minutes.

realm.subscriptions.update((mutableSubs) => {
mutableSubs.add(
tasks.filtered('status == "completed" && progressMinutes > 180'),
{
name: "longRunningTasksSubscription",
}
);
});

NOTE

Attempting to update a subscription that has


the SubscriptionOptions.throwOnUpdate field set to true throws an
exception.

API Reference
• MutableSubscriptionSet.add()

• SubScriptionSet.update()

• SubscriptionOptions.throwOnUpdate

Remove Subscriptions

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.

You can remove subscriptions in the following ways:

• Remove a single subscription with a specific query

• Remove a single subscription with a specific name

• Remove all subscriptions to a specific object model

• Remove all unnamed subscriptions

• Remove all subscriptions

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

Examples in this section 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 (
// ...
);
}

Remove a Subscription by Query

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"'));
});

Remove a Subscription by Name

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");
});

Remove a Subscription by Reference

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);
Remove All Subscriptions on an Object Type

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");
});

Remove All Unnamed Subscriptions

New in version [email protected] .

You may want to remove unnamed subscriptions that are transient or


dynamically generated, but leave named subscriptions in place.

You can remove all unnamed subscriptions from the subscription set by
calling .removeUnnamed() on mutableSubs. .removeUnnamed() returns the
number of unnamed subscriptions removed.

// Remove unnamed subscriptions.


let numberRemovedSubscriptions = 0;
await realm.subscriptions.update((mutableSubs) => {
numberRemovedSubscriptions = mutableSubs.removeUnnamed();
});

API Reference

• removeUnnamed()

Remove All Subscriptions

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();
});
Performance Considerations

API Efficiency

Managing multiple subscriptions with


the subscribe() and unsubscribe() APIs described in the Subscribe to
Queries section is less efficient than performing batch updates when you
manually manage subscriptions.

For better performance when making multiple subscription changes, use


the subscriptions API to update all the subscriptions in a single transaction.
To learn how, see Manually Manage Subscriptions.

Group Updates for Improved Performance

Every write transaction for a subscription set has a performance cost. If you
need to make multiple updates to a Realm object during a session, consider
keeping edited objects in memory until all changes are complete. This
improves sync performance by only writing the complete and updated object
to your realm instead of every change.

Flexible Sync RQL Requirements and Limitations

Indexed Queryable Fields Subscription Requirements

Adding an indexed queryable field to your App can improve performance for
simple queries on data that is strongly partitioned. For example, an app where
queries strongly map data to a device, store, or user, such as user_id ==
$0, “641374b03725038381d2e1fb”, is a good candidate for an indexed
queryable field. However, an indexed queryable field has specific requirements
for use in a query subscription:

• The indexed queryable field must be used in every subscription query. It


cannot be missing from the query.
• The indexed queryable field must use an == or IN comparison against a
constant at least once in the subscription query. For example, user_id
== $0, "641374b03725038381d2e1fb" or store_id IN $0,
{1,2,3}.

You can optionally include an AND comparison as long as the indexed


queryable field is directly compared against a constant using == or IN at least
once. For example, store_id IN {1,2,3} AND
region=="Northeast" or store_id == 1 AND (active_promotions < 5
OR num_employees < 10).

Invalid Flexible Sync queries on an indexed queryable field include queries


where:

• The indexed queryable field does not use AND with the rest of the query.
For example store_id IN {1,2,3} OR region=="Northeast" is
invalid because it uses OR instead of AND. Similarly, store_id == 1
AND active_promotions < 5 OR num_employees < 10 is invalid
because the AND only applies to the term next to it, not the entire query.

• The indexed queryable field is not used in an equality operator. For


example store_id > 2 AND region=="Northeast" is invalid
because it uses only the > operator with the indexed queryable field and
does not have an equality comparison.

• The query is missing the indexed queryable field entirely. For


example, region=="Northeast or truepredicate are invalid because
they do not contain the indexed queryable field.

Unsupported Query Operators in Flexible Sync

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

@avg, @count, @max, @min, @sum


Aggregate Operators

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.

List Queries

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"
Embedded or Linked Objects

Flexible Sync does not support querying on properties in Embedded Objects


or links. For example, obj1.field == "foo".

Manage a Sync Session - React Native SDK

Realm is now Atlas Device SDK – Learn More

When you use Atlas Device Sync, the React Native SDK syncs data with Atlas
in the background using a sync session. A sync session starts whenever you
open a synced realm.

Prerequisites

Before you can manage a sync session, you must perform the following:

1. Open a synced realm

2. Add a sync subscription

3. Wrap components that use the useRealm() hook for a synced realm
with the AppProvider, UserProvider,
and RealmProvider components. For more information on configuring
and opening a synced realm, refer to Open a Synced Realm.

Access Sync Session

Access a synced realm in a component with the useRealm() hook. Access


the sync session with the realm's Realm.syncSession property.

import React, {useEffect} from 'react';


import {Context} from '../RealmConfig';
const {useRealm} = Context;
function AccessSyncSession() {
const realm = useRealm();
async function workWithSyncSession() {
const {syncSession} = realm;
// Do stuff with sync session...
}
// ...
}

Pause or Resume a Sync Session

Opening a synced realm starts a sync session. You can pause and resume the
realm's sync session. If you have more than one open realm, pause does not
affect the other realms' sync sessions.

To pause synchronization, use the Realm.syncSession.pause() method. To


resume synchronization, use the Realm.syncSession.resume() method.

import React, {useEffect, useState} from 'react';


import {Context} from '../RealmConfig';
const {useRealm} = Context;
function ToggleSyncSession() {
const realm = useRealm();
const [isPaused, setIsPaused] = useState(false);
async function toggleSyncSession() {
if (isPaused) {
await realm.syncSession?.resume();
} else {
await realm.syncSession?.pause();
}
setIsPaused(!isPaused);
}
return (
<Button
title={isPaused ? 'Pause Sync' : 'Unpause Sync'}
onPress={toggleSyncSession}
/>
);
}

When to Pause a Sync Session

For most applications, there is no need to manually pause and resume a sync
session. However, there are a few circumstances under which you may want to
pause or suspend a sync session:
• You only want to sync after the user takes a specific action

• You only want to sync during a certain time of the day

• You don't want to attempt to sync when there is poor network


connectivity

• You want to explicitly force a sync session to connect

In the case of poor network connectivity, continually trying to establish a


network connection can drain the user's device battery.

The case of explicitly forcing a sync session to connect is most commonly


related to being offline for some time. The sync client attempts to connect, and
upon failure, goes into exponential backoff. After being offline for a long time,
the client may not immediately reconnect. Pausing and resuming the sync
session explicitly forces the connection.

When you do pause a sync session, keep these things in mind:

• If the client may be offline longer than the client maximum offline time,
the client will be unable to resume syncing and must perform a client
reset.

• Pausing a sync session pauses it in both directions. Changes that your


app makes on the device do not sync with the backend, and changes to
the data in the backend or on other devices do not sync to the device.
There is no way to pause only uploads or pause only downloads.

• Do not pause a sync session if you want a client to permanently stop


syncing with the backend. To permanently stop syncing, copy the
contents of the synced realm into a non-synced realm, and use the non-
synced realm in the client.

Do not pause sync 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. 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 Realm.syncSession.addProgressNotification() method.

The Realm.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 Realm.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 and transferable. 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.

The following example registers a callback on the syncSession to listen for


upload events indefinitely. The example writes to the realm and then
unregisters the syncSession notification callback.
TypeScript
JavaScript
import React, {useEffect, useState} from 'react';
import {SyncedRealmContext} from '../RealmConfig';
const {useRealm} = SyncedRealmContext;
import {Text} from 'react-native';
function CheckUploadProgress() {
const realm = useRealm();
const [uploadProgressPercent, setUploadProgressPercent] = useState(0);
useEffect(() => {
const progressNotificationCallback = (transferred, transferable) => {
// Convert decimal to percent with no decimals
// (e.g. 0.6666... -> 67)
const percentTransferred =
parseFloat((transferred / transferable).toFixed(2)) * 100;
setUploadProgressPercent(percentTransferred);
};
// Listen for changes to connection state
realm.syncSession?.addProgressNotification(
Realm.ProgressDirection.Upload,
Realm.ProgressMode.ReportIndefinitely,
progressNotificationCallback,
);
// Remove the connection listener when component unmounts
return () =>
realm.syncSession?.removeProgressNotification(
progressNotificationCallback,
);
// Run useEffect only when component mounts
}, []);
return <Text>Percent Uploaded: {uploadProgressPercent} %</Text>;
}

Check the Network Connection

Realm's offline-first design means that you generally don't need to check the
current network connection state since data syncs in the background when a
connection is available. That said, the Realm SDK provides methods to get the
current state of the network connection to the server.

To check the current state of the connection to the server,


call Realm.syncSession.isConnected(). This method returns a boolean that
is true if there is a network connection and the sync session is active.
To listen for connection state changes,
call Realm.syncSession.addConnectionNotification(), passing a callback
function to handle network changes as the argument. To unregister the
listener, pass the same callback function
to Realm.syncSession.removeConnectionNotification().

TypeScript
JavaScript
import React, {useState, useEffect} from 'react';
import {SyncedRealmContext} from '../RealmConfig';
const {useRealm} = SyncedRealmContext;
import {Text} from 'react-native';
function CheckNetworkConnection() {
const realm = useRealm();
const [isConnected, setIsConnected] = useState(
realm.syncSession?.isConnected(),
);
useEffect(() => {
const connectionNotificationCallback = (newState, oldState) => {
console.log('Current connection state: ' + newState);
console.log('Previous connection state: ' + oldState);
setIsConnected(realm.syncSession?.isConnected());
};
// Listen for changes to connection state
realm.syncSession?.addConnectionNotification(
connectionNotificationCallback,
);
// Remove the connection listener when component unmounts
return () =>
realm.syncSession?.removeConnectionNotification(
connectionNotificationCallback,
);
// Run useEffect only when component mounts
}, []);
return (
<Text>
{isConnected ? 'Connected to Network' : 'Disconnected from Network'}
</Text>
);
}

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.

import React, {useEffect} from 'react';


import {Context} from '../RealmConfig';
import {AppProvider, UserProvider, useUser, useApp, Realm} from '@realm/react';
function AppWrapper() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={<LogIn />}>
<RealmWrapper>
<RestOfApp />
</RealmWrapper>
</UserProvider>
</AppProvider>
);
}
type RealmWrapperProps = {
children: React.ReactNode;
};
function RealmWrapper({children}: RealmWrapperProps) {
const app = useApp();
Realm.App.Sync.enableSessionMultiplexing(app);
return <RealmProvider sync={{flexible: true}}>{children}</RealmProvider>;
}

Handle Sync Errors - React Native SDK

Realm is now Atlas Device SDK – Learn More

Add a Generic Sync Error Handler

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 FlexibleSyncConfiguration. If you're using @realm/react, pass
the SyncConfiguration object to the sync property of a RealmProvider.

const syncConfigWithErrorHandling = {
flexible: true,
onError: (_session, error) => {
console.log(error);
},
};
function RealmWithErrorHandling() {
return (
<RealmProvider sync={syncConfigWithErrorHandling}>
<RestOfApp />
</RealmProvider>
);
}

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.

Handle Client Reset Errors

A client reset error is a type of sync error where a client realm cannot sync
data with the Atlas App Services backend. Clients in this state may continue to
run and save data locally but cannot send or receive sync changesets until
they perform a client reset.

To learn about the causes of and modes for handling client resets, refer
to Device Sync Client Resets in the App Services documentation.
Client Reset Modes

You can specify which client reset mode your app should use to restore the
realm to a syncable state:

• Recover unsynced changes mode: When you choose this mode, the
client attempts to recover unsynced changes. Choose this mode when
you do not want to fall through to discard unsynced changes.

• Recover or discard unsynced changes mode: The client first attempts to


recover changes that have not yet synced. If the client cannot recover
unsynced data, it falls through to discard unsynced changes but
continues to automatically perform the client reset. Choose this mode
when you want to enable automatic client recovery to fall back to
discard unsynced changes.

• Discard unsynced changes mode: Restores the realm to a syncable


state by discarding changes made since the last sync.

• Manual recovery mode: Downloads a new copy of the realm, and moves
the unsyncable realm to a backup. Migrate unsynced data from the
backup copy of the realm to the new syncable copy.

Automatic vs. Manual Client Reset

The Realm SDKs provide client reset modes that automatically handle most
client reset errors.

Automatic client reset modes restore your local realm file to a syncable state
without closing the realm or missing notifications. The following client reset
modes support automatic client resets:

• Recover unsynced changes mode

• Recover or discard unsynced changes mode

• Discard unsynced changes mode


The differences between these modes are based on how they handle changes
on the device that have not yet synced to the backend. Only manual recovery
mode does not perform an automatic client reset.

Choose recover unsynced changes mode to handle most client reset


scenarios automatically. This attempts to recover unsynced changes when a
client reset occurs.

If your app requires specific client reset logic that can't be handled
automatically, you may want or need to add a manual client reset handler to
the automatic client reset mode.

Client Reset with Recovery

New in version [email protected] .

Client Recovery is a feature that is enabled by default when you configure


Device Sync. When Client Recovery is enabled, Realm automatically manages
the client reset process in most cases. The client can recover unsynced
changes when there are no schema changes, or non-breaking schema
changes.

To use Client Recovery, configure your realm with one of the following client
reset modes:

• Recover unsynced changes mode

• Recover or discard unsynced changes

When Client Recovery is enabled, these rules determine how objects are
integrated, including how conflicts are resolved when both the backend and
the client make changes to the same object:

• Objects created locally that were not synced before client reset are
synced.
• If an object is deleted on the server, but is modified on the recovering
client, the delete takes precedence and the client discards the update.

• If an object is deleted on the recovering client, but not the server, then
the client applies the server's delete instruction.

• In the case of conflicting updates to the same field, the client update is
applied.

For more information about configuring Client Recovery, refer to Client


Recovery in the App Services documentation.

Client Recovery cannot succeed when your app makes breaking schema
changes. A breaking change is a change that you can make in your server-side
schema that requires additional action to handle. In this scenario, client reset
falls back to a manual error client reset fallback.

For information on breaking vs. non-breaking schema changes, refer


to Breaking vs. Non-Breaking Change Quick Reference in the App Services
documentation.

Recover Unsynced Changes Mode

When you choose recover unsynced changes mode, the client attempts to
recover unsynced changes with Client Recovery. Choose this mode when you
do not want to fall through to discard unsynced changes.

To handle client resets with the recover unsynced changes mode, pass
a ClientResetConfiguration to the clientReset field of
your FlexibleSyncConfiguration. Include these properties in
the ClientResetConfiguration:

• mode: Set to "recoverUnsyncedChanges".

• onBefore: Optional. Callback function invoked before the SDK executes


this mode, when the SDK receives a client reset error from the backend.
Provides a copy of the realm.
• onAfter: Optional. Callback function invoked after the SDK
successfully executes this mode. Provides instances of the realm before
and after the client reset.

• onFallback: Optional. Callback function which the SDK invokes only if


the automatic recovery fails. For more information, refer to the Manual
Client Reset Fallback section.

The following example implements recover unsynced changes mode:

TypeScript
JavaScript
const syncConfigWithRecoverClientReset = {
flexible: true,
clientReset: {
mode: 'recoverUnsyncedChanges',
onBefore: realm => {
// This block could be used for custom recovery, reporting, debugging etc.
},
onAfter: (beforeRealm, afterRealm) => {
// This block could be used for custom recovery, reporting, debugging etc.
},
onFallback: (session, path) => {
// See below "Manual Client Reset Fallback" section for example
},
},
};
function RealmWithRecoverUnsyncedChangesClientReset() {
return (
<RealmProvider sync={syncConfigWithRecoverClientReset}>
<RestOfApp />
</RealmProvider>
);
}

Recover or Discard Unsynced Changes Mode

In recover or discard unsynced changes mode, the client first attempts to


recover changes that have not yet synced. If the client cannot recover
unsynced data, it falls through to discard unsynced changes but continues to
automatically perform the client reset. Choose this mode when you want to
enable automatic client recovery to fall back to discard unsynced changes.

Do not use recover or discard unsynced changes mode if your application


cannot lose local data that has not yet synced to the backend.

To handle client resets with the recover or discard unsynced changes mode,
pass a ClientResetConfiguration to the clientReset field of
your FlexibleSyncConfiguration. Include these properties in
the ClientResetConfiguration:

• mode: Set to "recoverOrDiscardUnsyncedChanges".

• onBefore: Optional. Callback function invoked before the SDK executes


this mode, when the SDK receives a client reset error from the backend.
Provides a copy of the realm.

• onAfter: Optional. Callback function invoked after the SDK


successfully executes this mode. Provides instances of the realm before
and after the client reset.

• onFallback(): Optional. Callback function which the SDK invokes only


if both the automatic recovery and and discarding changes fails. For
more information, refer to the Manual Client Reset Fallback section.

The following example implements recover unsynced changes mode:

TypeScript
JavaScript
const syncConfigWithRecoverDiscardClientReset = {
flexible: true,
clientReset: {
mode: 'recoverOrDiscardUnsyncedChanges',
onBefore: realm => {
// This block could be used for custom recovery, reporting, debugging etc.
},
onAfter: (beforeRealm, afterRealm) => {
// This block could be used for custom recovery, reporting, debugging etc.
},
onFallback: (session, path) => {
// See below "Manual Client Reset Fallback" section for example
},
},
};
function RealmWithRecoverOrDiscardUnsyncedChangesClientReset() {
return (
<RealmProvider sync={syncConfigWithRecoverDiscardClientReset}>
<RestOfApp />
</RealmProvider>
);
}

Manual Client Reset Fallback

If the client reset with recovery cannot complete automatically, like when there
are breaking schema changes, the client reset process falls through to a
manual error handler. This may occur in either of the client reset with recovery
modes, recover unsynced changes and recover or discard unsynced changes.
You must provide a manual client reset implementation in
the SyncConfiguration.onFallback() callback. onFallback() takes two
arguments:

• session: Session object representing the state of the Device Sync


session.

• path: String with the path to the current realm file.

The following example demonstrates how you can manually handle this error
case by discarding all unsynced changes:

TypeScript
JavaScript
let realm; // value assigned in <RestOfApp> with useRealm()
const syncConfigWithClientResetFallback = {
flexible: true,
clientReset: {
mode: 'recoverOrDiscardUnsyncedChanges', // or "recoverUnsyncedChanges"
// can also include `onBefore` and `onAfter` callbacks
onFallback: (_session, path) => {
try {
// Prompt user to perform a client reset immediately. If they don't,
// they won't receive any data from the server until they restart the app
// and all changes they make will be discarded when the app restarts.
const didUserConfirmReset = showUserAConfirmationDialog();
if (didUserConfirmReset) {
// Close and delete old realm from device
realm.close();
Realm.deleteFile(path);
// Perform client reset
Realm.App.Sync.initiateClientReset(app, path);
// Navigate the user back to the main page or reopen the
// the Realm and reinitialize the current page
}
} catch (err) {
// Reset failed. Notify user that they'll need to
// update the app
}
},
},
};
function RealmWithManualClientResetFallback() {
return (
<RealmProvider sync={syncConfigWithClientResetFallback}>
<RestOfApp />
</RealmProvider>
);
}
function RestOfApp() {
// Assigning variable defined above to a realm.
realm = useRealm();
return <>{/* Other components in rest of app */}</>;
}

Discard Unsynced Changes Mode

New in version [email protected] .

Changed in version [email protected] : Mode renamed from "discardLocal" to


"discardUnsyncedChanges". Both currently work, but in a future version,
"discardLocal" will be removed. "clientResetBefore" and "clientResetAfter" callbacks
renamed to "onBefore" and "onAfter", respectively.
Discard Unsynced Changes mode permanently deletes all local unsynced
changes made since the last successful sync. You might use this mode when
your app requires client recovery logic that is not consistent with automatic
Client Recovery, or when you don't want to recover unsynced data.

Do not use discard unsynced changes mode if your application cannot lose
local data that has not yet synced to the backend.

To handle client resets with the discard unsynced changes mode, pass
a ClientResetConfiguration to the clientReset field of
your FlexibleSyncConfiguration. Include these properties in
the ClientResetConfiguration:

• mode: Set to "discardUnsyncedChanges".

• onBefore: Optional. Callback function invoked before the SDK executes


this mode, when the SDK receives a client reset error from the backend.
Provides a copy of the realm.

• onAfter: Optional. Callback function invoked after the SDK


successfully executes this mode. Provides instances of the realm before
and after the client reset.

The following example implements discard unsynced changes mode:

TypeScript
JavaScript
const syncConfigWithDiscardClientReset = {
flexible: true,
clientReset: {
mode: 'discardUnsyncedChanges',
onBefore: realm => {
console.log('Beginning client reset for ', realm.path);
},
onAfter: (beforeRealm, afterRealm) => {
console.log('Finished client reset for', beforeRealm.path);
console.log('New realm path', afterRealm.path);
},
},
};
function RealmWitDiscardUnsyncedChangesClientReset() {
return (
<RealmProvider sync={syncConfigWithDiscardClientReset}>
<RestOfApp />
</RealmProvider>
);
}

Discard Unsynced Changes after Breaking Schema Changes

If your application experiences a breaking schema change, discard unsynced


changes mode cannot handle the resulting client reset automatically. Instead,
you must provide a manual client reset implementation in the
SyncConfiguration error() callback. The following example demonstrates
how you can manually handle this error case by discarding all unsynced
changes:

TypeScript
JavaScript
// Once you have opened your Realm, you will have to keep a reference to it.
// In the error handler, this reference is called `realm`
async function handleSyncError(session, syncError) {
if (syncError.name == 'ClientReset') {
console.log(syncError);
try {
console.log('error type is ClientReset....');
const path = realm.path; // realm.path will not be accessible after realm.close()
realm.close();
Realm.App.Sync.initiateClientReset(app, path);
// Download Realm from the server.
// Ensure that the backend state is fully downloaded before proceeding,
// which is the default behavior.
realm = await Realm.open(config);
realm.close();
} catch (err) {
console.error(err);
}
} else {
// ...handle other error types
}
}
const syncConfigWithDiscardAfterBreakingSchemaChanges = {
flexible: true,
clientReset: {
mode: 'discardUnsyncedChanges',
onBefore: realm => {
// NOT used with destructive schema changes
console.log('Beginning client reset for ', realm.path);
},
onAfter: (beforeRealm, afterRealm) => {
// Destructive schema changes do not hit this function.
// Instead, they go through the error handler.
console.log('Finished client reset for', beforeRealm.path);
console.log('New realm path', afterRealm.path);
},
},
onError: handleSyncError, // invoked with destructive schema changes
};
function RealmWitDiscardAfterBreakingSchemaChangesClientReset() {
return (
<RealmProvider sync={syncConfigWithDiscardAfterBreakingSchemaChanges}>
<RestOfApp />
</RealmProvider>
);
}

NOTE

Discard with Recovery

If you'd like to attempt to recover unsynced changes, but but discard any
changes that cannot be recovered, refer to the recover or discard unsynced
changes mode section.

Manual Mode

Changed in version [email protected] : onManual callback added

In manual mode, you define your own client reset handler. You might want to
use a manual client reset handler if the Automatic Recovery logic does not
work for your app and you can't discard unsynced local data.

To handle client resets with manual mode, pass a ClientResetConfiguration to


the clientReset field of your FlexibleSyncConfiguration. Include these
properties in the ClientResetConfiguration:

• mode: Set to "manual".

• onManual: Optional. Callback function invoked when the client reset


occurs. Provides information about the sync session and the path to the
current realm. If you don't set the onManual error handler, the client
reset error falls back to the general sync error handler.
TypeScript
JavaScript
const syncConfigWithManualClientReset = {
flexible: true,
clientReset: {
mode: 'manual',
onManual: (session, path) => {
// handle manual client reset here
},
},
};
function RealmWitManualClientReset() {
return (
<RealmProvider sync={syncConfigWithManualClientReset}>
<RestOfApp />
</RealmProvider>
);
}

Manual Data Recovery

To recover data from a manual client reset requires significant amounts of


code, schema concessions, and custom conflict resolution logic. If you need
to implement your own custom client reset logic, see the Advanced Guide to
Manual Client Reset Data Recovery.

Test Client Reset Handling


You can manually test your application's client reset handling by terminating
and re-enabling Device Sync.

When you terminate and re-enable Sync, clients that have previously
connected with Sync are unable to connect until after they perform a client
reset. Terminating Sync deletes the metadata from the server that allows the
client to synchronize. The client must download a new copy of the realm from
the server. The server sends a client reset error to these clients. So, when you
terminate Sync, you trigger the client reset condition.

To test client reset handling:

1. Write data from a client application and wait for it to synchronize.

2. Terminate and re-enable Device Sync.

3. Run the client app again. The app should get a client reset error when it
tries to connect to the server.

WARNING

While you iterate on client reset handling in your client application, you may
need to terminate and re-enable Sync repeatedly. Terminating and re-enabling
Sync renders all existing clients unable to sync until after completing a client
reset. To avoid this in production, test client reset handling in a
development environment.

Manual Client Reset Data Recovery - React Native SDK

Realm is now Atlas Device SDK – Learn More

This page explains how to manually recover unsynced realm data after a client
reset using the Manual Recovery client reset mode.

Manual recovery requires significant amounts of code, schema concessions,


and custom conflict resolution logic. You should only perform a manual
recovery of unsynced realm data if you cannot lose unsynced data and the
other automatic client reset methods do not meet your use case.

For more information about the other available client reset modes, refer
to Reset a Client Realm.

The specifics of manual recovery depend heavily upon your application and
your schema. However, there are a few techniques that can help with manual
recoveries. The Track Changes by Object Strategy section demonstrates one
method of recovering unsynced changes during a client reset.

WARNING

Avoid Making Breaking Schema Changes in Production

Do not expect to recover all unsynced data after a breaking schema change.
The best way to preserve user data is to never make a breaking schema
change at all.

IMPORTANT

Breaking Schema Changes Require an App Schema Update

After a breaking schema change:

• All clients must perform a client reset.

• You must update client models affected by the breaking schema


change.

Track Changes by Object Strategy

The track changes by object manual client reset data recovery strategy lets
you recover data already written to the client realm file but not yet synced to
the backend.
In this strategy, you add a "Last Updated Time" to each object model to track
when each object last changed. We'll watch the to determine when the realm
last uploaded its state to the backend.

When backend invokes a client reset, find objects that were deleted, created,
or updated since the last sync with the backend. Then copy that data from the
backup realm to the new realm.

The following steps demonstrate implementing the process at a high level:

1. Client reset error: Your application receives a client reset error code
from the backend.

2. Strategy implementation: The SDK calls your strategy implementation.

3. Close all instances of the realm: Close all open instances of the realm
experiencing the client reset. If your application architecture makes this
difficult (for instance, if your app uses many realm instances
simultaneously in listeners throughout the application), it may be easier
to restart the application. You can do this programmatically or through a
direct request to the user in a dialog.

4. Move the realm to a backup file: Call


the Realm.App.Sync.initiateClientReset() static method. This method
moves the current copy of the client realm file to a backup file.

5. Open new instance of the realm: Open a new instance of the realm using
your typical sync configuration. If your application uses multiple realms,
you can identify the realm experiencing a client reset from the backup
file name.

6. Download all realm data from the backend: Download the entire set of
data in the realm before you proceed. This is the default behavior of
the FlexibleSyncConfiguration object.

7. Open the realm backup: Use the error.config object passed as an


argument to the SyncConfiguration.error callback function.
8. Migrate unsynced changes: Query the backup realm for data to recover.
Insert, delete or update data in the new realm accordingly.

Example

This example demonstrates implementing the track changes by object manual


client reset data recovery strategy.

NOTE

Limitations of This Example

• This example only is for an application with a single realm containing a


single Realm object type. For each additional object type, you'd need to
add another sync listener as described in the Track Synchronization in
Separate Realm section.

• This example keeps track of the last time each object was updated. As a
result, the recovery operation overwrites the entire object in the new
realm if any field was updated after the last successful sync of the
backup realm. This could overwrite fields updated by other clients with
old data from this client. If your realm objects contain multiple fields
containing important data, consider keeping track of the last updated
time of each field instead, and recovering each field individually.

For more information on other ways to perform a manual client reset with data
recovery, refer to the Alternative Strategies section.

Include Last Updated Time in Your Schema

Add a new property to your Realm object schema to track the last time it was
updated. Whenever you create or update a Realm object with the schema,
include a timestamp with the update time.
Ordinarily, there is no way to detect when a Realm object was last modified.
This makes it difficult to determine which changes were synced to the
backend. By adding a timestamp lastUpdated to your Realm object models
and updating that timestamp to the current time whenever a change occurs,
you can keep track of when objects were changed.

const DogSchema = {
name: "Dog",
properties: {
name: "string",
age: "int?",

lastUpdated: "int",

},
};
2

Configure Realm to Use Manual Client Reset

In the realm's FlexibleSyncConfiguration, set the clientReset field to manual


mode and include an error callback function. You'll define the error callback
function in the Create Callback to Handle Client Reset section.

const config = {
schema: [DogSchema],
sync: {
user: app.currentUser,
partitionValue: "MyPartitionValue",

clientReset: {
mode: "manual",
},
error: handleSyncError, // callback function defined later

},
};
3

Track Synchronization in Separate Realm

Just knowing when objects were changed isn't enough to recover data during
a client reset. You also need to know when the realm last completed a sync
successfully. This example implementation uses a singleton object in a
separate realm called LastSynced paired with a change listener to record
when a realm finishes syncing successfully.

Define your LastSynced Realm to track the latest time your realm
synchronizes.

const LastSyncedSchema = {
name: "LastSynced",
properties: {
realmTracked: "string",
timestamp: "int?",
},
primaryKey: "realmTracked",
};
const lastSyncedConfig = { schema: [LastSyncedSchema] };
const lastSyncedRealm = await Realm.open(lastSyncedConfig);
lastSyncedRealm.write(() => {
lastSyncedRealm.create("LastSynced", {
realmTracked: "Dog",
});
});

Register a change listener to subscribe to changes to the Dog collection. Only


update the LastSynced object if the sync session is connected and all local
changes have been synced with the server.
// Listens for changes to the Dogs collection
realm.objects("Dog").addListener(async () => {
// only update LastSynced if sync session is connected
// and all local changes are synced
if (realm.syncSession.isConnected()) {
await realm.syncSession.uploadAllLocalChanges();
lastSyncedRealm.write(() => {
lastSyncedRealm.create("LastSynced", {
realmTracked: "Dog",
timestamp: Date.now(),
});
});
}
});
4

Create Callback to Handle Client Reset

Now that you've recorded update times for all objects in your application as
well as the last time your application completed a sync, it's time to implement
the manual recovery process. This example handles two main recovery
operations:

• Restore unsynced inserts and updates from the backup realm

• Delete objects from the new realm that were previously deleted from the
backup realm

You can follow along with the implementation of these operations in the code
samples below.

async function handleSyncError(_session, error) {


if (error.name === "ClientReset") {
const realmPath = realm.path; // realm.path will not be accessible after realm.close()
realm.close(); // you must close all realms before proceeding
// pass your realm app instance and realm path to initiateClientReset()
Realm.App.Sync.initiateClientReset(app, realmPath);
// Redownload the realm
realm = await Realm.open(config);
const oldRealm = await Realm.open(error.config);
const lastSyncedTime = lastSyncedRealm.objectForPrimaryKey(
"LastSynced",
"Dog"
).timestamp;
const unsyncedDogs = oldRealm
.objects("Dog")
.filtered(`lastUpdated > ${lastSyncedTime}`);
// add unsynced dogs to synced realm
realm.write(() => {
unsyncedDogs.forEach((dog) => {
realm.create("Dog", dog, "modified");
});
});
// delete dogs from synced realm that were deleted locally
const syncedDogs = realm
.objects("Dog")
.filtered(`lastUpdated <= ${lastSyncedTime}`);
realm.write(() => {
syncedDogs.forEach((dog) => {
if (!oldRealm.objectForPrimaryKey("Dog", dog._id)) {
realm.delete(dog);
}
});
});
// make sure everything syncs and close old realm
await realm.syncSession.uploadAllLocalChanges();
oldRealm.close();
} else {
console.log(`Received error ${error.message}`);
}
}

Alternative Strategies

Possible alternate implementations include:

• Overwrite the entire backend with the backup state: With no "last
updated time" or "last synced time", upsert all objects from the backup
realm into the new realm. There is no way to recovered unsynced
deletions with this approach. This approach overwrites all data written
to the backend by other clients since the last sync. Recommended for
applications where only one user writes to each realm.

• Track changes by field: Instead of tracking a "last updated time" for


every object, track the "last updated time" for every field. Update fields
individually using this logic to avoid overwriting field writes from other
clients with old data. Recommended for applications with many fields
per-object where conflicts must be resolved at the field level.

• Track updates separately from objects: Instead of tracking a "last


updated time" in the schema of each object, create another model in
your schema called Updates. Every time any field in any object
(besides Updates) updates, record the primary key, field, and time of
the update. During a client reset, "re-write" all of the Update events that
occurred after the "last synced time" using the latest value of that field
in the backup realm. This approach should replicate all unsynced local
changes in the new realm without overwriting any fields with stale data.
However, storing the collection of updates could become expensive if
your application writes frequently. Recommended for applications
where adding "lastUpdated" fields to object models is undesirable.

Partition-Based Sync - React Native SDK

Realm is now Atlas Device SDK – Learn More

Partition-Based Sync is an older mode for using Atlas Device Sync with the
Realm React Native SDK. We recommend using Flexible Sync for new apps.
The information on this page is for users who are still using Partition-Based
Sync.

For more information about Partition-Based Sync and how to configure it in


Atlas App Services, refer to Partition-Based Sync in the App Services
documentation.

Partition Value

When you select Partition-Based Sync for your backend App configuration,
your client implementation must include a partition value. This is the value of
the partition key field you select when you configure Partition-Based Sync.

The partition value determines which data the client application can access.

You pass in the partition value when you open a synced realm.

Configure a Partition-Based Sync Realm

To open a Flexible Sync realm,


use @realm/react's createRealmContext() function and its
returned RealmProvider.
In a RealmProvider that's nested in a UserProvider, add a sync property
with a SyncConfiguration object that contains flexible: true.

Note that UserProvider automatically passes an authenticated user


to RealmProvider.

<RealmProvider
schema={[YourObjectModel]}
sync={{
partitionValue: 'testPartition',
}}>
<RestOfApp />
</RealmProvider>

Copy Data and Open a New Realm

New in version [email protected] .

To copy data from an existing realm to a new realm with different configuration
options, pass the new configuration the Realm.writeCopyTo() method.

NOTE

Same-Type Sync Only

This method only supports copying a Partition-Based Sync configuration for


another Partition-Based Sync user, or a Flexible Sync configuration for
another Flexible Sync user. You cannot use this method to convert between a
Partition-Based Sync realm and a Flexible Sync realm or vice-versa.

In the new realm's configuration, you must specify the path.

If you write the copied realm to a realm file that already exists, the data is
written object by object. The copy operation replaces objects if there already
exists objects for given primary keys. The schemas of the realm you copy and
the realm you are writing to must be compatible for the copy operation to
succeed. Only objects in the schemas of both configurations are copied over.

The configuration change can include modifications to SyncConfiguration:

• Local realm to synced realm

• Synced Realm to local realm

The configuration change can also include changes


to encryptionKey property of the Configuration:

• Encrypted realm to unencrypted realm

• Unencrypted realm to encrypted realm

EXAMPLE

Convert Local Realm to Synced Realm

TypeScript

JavaScript
const localConfig = {
schema: [Car],
path: "localOnly.realm",
};
const localRealm = await Realm.open(localConfig);
const syncedConfig = {
schema: [Car],
path: "copyLocalToSynced.realm",
sync: {
user: app.currentUser,
partitionValue: "myPartition",
},
};
localRealm.writeCopyTo(syncedConfig);
const syncedRealm = await Realm.open(syncedConfig);

You can also combine changes to configuration. For example, you can open a
local encrypted realm as a synced unencrypted realm or a unencrypted synced
realm as an encrypted synced realm.

EXAMPLE

Convert Synced Encrypted to Local Unencrypted Realm

TypeScript

JavaScript
// Create a secure key.
const encryptionKey = new Int8Array(64);
// ... store key
const syncedEncryptedConfig = {
schema: [Car],
path: "syncedEncrypted.realm",
sync: {
user: app.currentUser,
partitionValue: "myPartition",
},
encryptionKey,
};
const syncedEncryptedRealm = await Realm.open(syncedEncryptedConfig);
const localUnencryptedConfig = {
schema: [Car],
path: "copyLocalUnencrypted.realm",
};
syncedEncryptedRealm.writeCopyTo(localUnencryptedConfig);
const localUnencryptedRealm = await Realm.open(syncedEncryptedConfig);

Migrate from Partition-Based Sync to Flexible Sync

You can migrate your App Services Device Sync Mode from Partition-Based
Sync to Flexible Sync. Migrating is an automatic process that does not require
any changes to your application code. Automatic migration requires Realm
Node.js SDK version 11.10.0 or newer.

Migrating enables you to keep your existing App Services users and
authentication configuration. Flexible Sync provides more versatile
permissions configuration options and more granular data synchronization.

For more information about how to migrate your App Services App from
Partition-Based Sync to Flexible Sync, refer to Migrate Device Sync Modes.

Updating Client Code After Migration

The automatic migration from Partition-Based Sync to Flexible Sync does not
require any changes to your client code. However, to support this
functionality, Realm automatically handles the differences between the two
Sync Modes by:

• Automatically creating Flexible Sync subscriptions for each object type


where partitionKey == partitionValue.

• Injecting a partitionKey field into every object if one does not already
exist. This is required for the automatic Flexible Sync subscription.

If you need to make updates to your client code after migration, consider
updating your client codebase to remove hidden migration functionality. You
might want update your client codebase when:

• You add a new model or change a model in your client codebase


• You add or change functionality that involves reading or writing Realm
objects

• You want to implement more fine-grained control over what data you
sync

Make these changes to convert your Partition-Based Sync client code to use
Flexible Sync:

• Add flexible:true to your SyncConfiguration object where you open


a synced realm.

• Add relevant properties to your object models to use in your Flexible


Sync subscriptions. For example, you might add an ownerId property
to enable a user to sync only their own data.

• Remove automatic Flexible Sync subscriptions and manually create the


relevant subscriptions.

For examples of Flexible Sync permissions strategies, including examples of


how to model data for these strategies, refer to Device Sync Permissions
Guide.

Remove and Manually Create Subscriptions

When you migrate from Partition-Based Sync to Flexible Sync, Realm


automatically creates hidden Flexible Sync subscriptions for your app. The
next time you add or change subscriptions, we recommend that you:

1. Remove the automatically-generated subscriptions.

2. Manually add the relevant subscriptions in your client codebase.

This enables you to see all of your subscription logic together in your
codebase for future iteration and debugging.

For more information about the automatically-generated Flexible Sync


subscriptions, refer to Migrate Client App to Flexible Sync.
Set the Client Log Level - React Native SDK

Realm is now Atlas Device SDK – Learn More

Changed in version [email protected]: Deprecated in favor of Realm Logger

WARNING

This page shows how to set a Sync client log level in Realm React Native SDK
versions 11.9.0 and earlier. Realm React Native SDK v12.0.0 supersedes this
logging implementation with a Realm logger you can set and configure for
your application. For information on how to set a Realm logger in a later
version, refer to Logging - React Native SDK.

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.

Stream Data to Atlas - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can use Data Ingest to stream data from the client application to a Flexible
Sync-enabled Atlas App Services App.
You might want to sync data unidirectionally in IoT applications, such as a
weather sensor sending data to the cloud. Data Ingest is also useful for writing
other types of immutable data where you do not require conflict resolution,
such as creating invoices from a retail app or logging application events.

Data Ingest is optimized to provide performance improvements for heavy


client-side insert-only workloads.

Sync Data Unidirectionally from a Client Application


1

Define an Asymmetric Object

Asymmetric objects sync data unidirectionally. Define an asymmetric object


by setting asymmetric to true in your object model:

TypeScript
JavaScript
class WeatherSensor extends Realm.Object{
static schema = {
name: 'WeatherSensor',
// sync WeatherSensor objects one way from your device
// to your Atlas database.
asymmetric: true,
primaryKey: '_id',
properties: {
_id: 'objectId',
deviceId: 'string',
temperatureInFahrenheit: 'int',
barometricPressureInHg: 'float',
windSpeedInMph: 'float',
},
};
}

For more information on how to define an asymmetric object, refer to Define


an Asymmetric Object.

Connect and Authenticate with an App Services App


To stream data from the client to your backend App, you must connect to an
App Services backend and authenticate a user.

function LogIn() {
const app = useApp();
useEffect(() => {
app.logIn(Realm.Credentials.anonymous());
}, []);
return <></>;
}

Data Ingest is a feature of Flexible Sync, so the App you connect to must
use Flexible Sync.

Open a Realm

After you have an authenticated user, you can open a synced realm using a
Flexible Sync configuration object.

TypeScript
JavaScript
// Create a configuration object
const realmConfig = { schema: [WeatherSensor] };
// Create a realm context
const {RealmProvider, useRealm, useObject, useQuery} =
createRealmContext(realmConfig);
// Expose a sync realm
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
sync={{
flexible: true,
onError: console.error
}}>
<App />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}
Unlike bi-directional Sync, Data Ingest does not use a Flexible Sync
subscription.

You cannot query an asymmetric object or write it to a local realm, so


asymmetric objects are incompatible with bi-directional Flexible Sync,
Partition-Based Sync, or local Realm use.

Create Asymmetric Objects

Once you have an open Realm, you can create an asymmetric object inside a
write transaction using Realm.create(). When creating an asymmetric
object, Realm.create() returns undefined rather than the object itself.

const App = () => {


// Getting access to our opened realm instance
const realm = useRealm();
const handleAddSensor = () => {
realm.write(() => {
realm.create('WeatherSensor', {
_id: weatherSensorPrimaryKey,
deviceId: "WX1278UIT",
temperatureInFahrenheit: 66.7,
barometricPressureInHg: 29.65,
windSpeedInMph: 2,
});
});
}
return (
<Button
title='Add A New Sensor'
onPress={() => handleAddSensor()}
/>
)
};

You cannot read these objects. Once created, they sync to the App Services
backend and the linked Atlas database.

Atlas Device Sync completely manages the lifecycle of this data. It is


maintained on the device until Data Ingest synchronization is complete, and
then removed from the device.
Stream Data to Atlas - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can use Data Ingest to stream data from the client application to a Flexible
Sync-enabled Atlas App Services App.

You might want to sync data unidirectionally in IoT applications, such as a


weather sensor sending data to the cloud. Data Ingest is also useful for writing
other types of immutable data where you do not require conflict resolution,
such as creating invoices from a retail app or logging application events.

Data Ingest is optimized to provide performance improvements for heavy


client-side insert-only workloads.

Sync Data Unidirectionally from a Client Application


1

Define an Asymmetric Object

Asymmetric objects sync data unidirectionally. Define an asymmetric object


by setting asymmetric to true in your object model:

TypeScript
JavaScript
class WeatherSensor extends Realm.Object{
static schema = {
name: 'WeatherSensor',
// sync WeatherSensor objects one way from your device
// to your Atlas database.
asymmetric: true,
primaryKey: '_id',
properties: {
_id: 'objectId',
deviceId: 'string',
temperatureInFahrenheit: 'int',
barometricPressureInHg: 'float',
windSpeedInMph: 'float',
},
};
}
For more information on how to define an asymmetric object, refer to Define
an Asymmetric Object.

Connect and Authenticate with an App Services App

To stream data from the client to your backend App, you must connect to an
App Services backend and authenticate a user.

function LogIn() {
const app = useApp();
useEffect(() => {
app.logIn(Realm.Credentials.anonymous());
}, []);
return <></>;
}

Data Ingest is a feature of Flexible Sync, so the App you connect to must
use Flexible Sync.

Open a Realm

After you have an authenticated user, you can open a synced realm using a
Flexible Sync configuration object.

TypeScript
JavaScript
// Create a configuration object
const realmConfig = { schema: [WeatherSensor] };
// Create a realm context
const {RealmProvider, useRealm, useObject, useQuery} =
createRealmContext(realmConfig);
// Expose a sync realm
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
sync={{
flexible: true,
onError: console.error
}}>
<App />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

Unlike bi-directional Sync, Data Ingest does not use a Flexible Sync
subscription.

You cannot query an asymmetric object or write it to a local realm, so


asymmetric objects are incompatible with bi-directional Flexible Sync,
Partition-Based Sync, or local Realm use.

Create Asymmetric Objects

Once you have an open Realm, you can create an asymmetric object inside a
write transaction using Realm.create(). When creating an asymmetric
object, Realm.create() returns undefined rather than the object itself.

const App = () => {


// Getting access to our opened realm instance
const realm = useRealm();
const handleAddSensor = () => {
realm.write(() => {
realm.create('WeatherSensor', {
_id: weatherSensorPrimaryKey,
deviceId: "WX1278UIT",
temperatureInFahrenheit: 66.7,
barometricPressureInHg: 29.65,
windSpeedInMph: 2,
});
});
}
return (
<Button
title='Add A New Sensor'
onPress={() => handleAddSensor()}
/>
)
};

You cannot read these objects. Once created, they sync to the App Services
backend and the linked Atlas database.
Atlas Device Sync completely manages the lifecycle of this data. It is
maintained on the device until Data Ingest synchronization is complete, and
then removed from the device.

Stream Data to Atlas - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can use Data Ingest to stream data from the client application to a Flexible
Sync-enabled Atlas App Services App.

You might want to sync data unidirectionally in IoT applications, such as a


weather sensor sending data to the cloud. Data Ingest is also useful for writing
other types of immutable data where you do not require conflict resolution,
such as creating invoices from a retail app or logging application events.

Data Ingest is optimized to provide performance improvements for heavy


client-side insert-only workloads.

Sync Data Unidirectionally from a Client Application


1

Define an Asymmetric Object

Asymmetric objects sync data unidirectionally. Define an asymmetric object


by setting asymmetric to true in your object model:

TypeScript
JavaScript
class WeatherSensor extends Realm.Object{
static schema = {
name: 'WeatherSensor',
// sync WeatherSensor objects one way from your device
// to your Atlas database.
asymmetric: true,
primaryKey: '_id',
properties: {
_id: 'objectId',
deviceId: 'string',
temperatureInFahrenheit: 'int',
barometricPressureInHg: 'float',
windSpeedInMph: 'float',
},
};
}

For more information on how to define an asymmetric object, refer to Define


an Asymmetric Object.

Connect and Authenticate with an App Services App

To stream data from the client to your backend App, you must connect to an
App Services backend and authenticate a user.

function LogIn() {
const app = useApp();
useEffect(() => {
app.logIn(Realm.Credentials.anonymous());
}, []);
return <></>;
}

Data Ingest is a feature of Flexible Sync, so the App you connect to must
use Flexible Sync.

Open a Realm

After you have an authenticated user, you can open a synced realm using a
Flexible Sync configuration object.

TypeScript
JavaScript
// Create a configuration object
const realmConfig = { schema: [WeatherSensor] };
// Create a realm context
const {RealmProvider, useRealm, useObject, useQuery} =
createRealmContext(realmConfig);
// Expose a sync realm
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
sync={{
flexible: true,
onError: console.error
}}>
<App />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

Unlike bi-directional Sync, Data Ingest does not use a Flexible Sync
subscription.

You cannot query an asymmetric object or write it to a local realm, so


asymmetric objects are incompatible with bi-directional Flexible Sync,
Partition-Based Sync, or local Realm use.

Create Asymmetric Objects

Once you have an open Realm, you can create an asymmetric object inside a
write transaction using Realm.create(). When creating an asymmetric
object, Realm.create() returns undefined rather than the object itself.

const App = () => {


// Getting access to our opened realm instance
const realm = useRealm();
const handleAddSensor = () => {
realm.write(() => {
realm.create('WeatherSensor', {
_id: weatherSensorPrimaryKey,
deviceId: "WX1278UIT",
temperatureInFahrenheit: 66.7,
barometricPressureInHg: 29.65,
windSpeedInMph: 2,
});
});
}
return (
<Button
title='Add A New Sensor'
onPress={() => handleAddSensor()}
/>
)
};

You cannot read these objects. Once created, they sync to the App Services
backend and the linked Atlas database.

Atlas Device Sync completely manages the lifecycle of this data. It is


maintained on the device until Data Ingest synchronization is complete, and
then removed from the device.

Stream Data to Atlas - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can use Data Ingest to stream data from the client application to a Flexible
Sync-enabled Atlas App Services App.

You might want to sync data unidirectionally in IoT applications, such as a


weather sensor sending data to the cloud. Data Ingest is also useful for writing
other types of immutable data where you do not require conflict resolution,
such as creating invoices from a retail app or logging application events.

Data Ingest is optimized to provide performance improvements for heavy


client-side insert-only workloads.

Sync Data Unidirectionally from a Client Application


1

Define an Asymmetric Object

Asymmetric objects sync data unidirectionally. Define an asymmetric object


by setting asymmetric to true in your object model:

TypeScript
JavaScript
class WeatherSensor extends Realm.Object{
static schema = {
name: 'WeatherSensor',
// sync WeatherSensor objects one way from your device
// to your Atlas database.
asymmetric: true,
primaryKey: '_id',
properties: {
_id: 'objectId',
deviceId: 'string',
temperatureInFahrenheit: 'int',
barometricPressureInHg: 'float',
windSpeedInMph: 'float',
},
};
}

For more information on how to define an asymmetric object, refer to Define


an Asymmetric Object.

Connect and Authenticate with an App Services App

To stream data from the client to your backend App, you must connect to an
App Services backend and authenticate a user.

function LogIn() {
const app = useApp();
useEffect(() => {
app.logIn(Realm.Credentials.anonymous());
}, []);
return <></>;
}

Data Ingest is a feature of Flexible Sync, so the App you connect to must
use Flexible Sync.

Open a Realm

After you have an authenticated user, you can open a synced realm using a
Flexible Sync configuration object.

TypeScript
JavaScript
// Create a configuration object
const realmConfig = { schema: [WeatherSensor] };
// Create a realm context
const {RealmProvider, useRealm, useObject, useQuery} =
createRealmContext(realmConfig);
// Expose a sync realm
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
sync={{
flexible: true,
onError: console.error
}}>
<App />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

Unlike bi-directional Sync, Data Ingest does not use a Flexible Sync
subscription.

You cannot query an asymmetric object or write it to a local realm, so


asymmetric objects are incompatible with bi-directional Flexible Sync,
Partition-Based Sync, or local Realm use.

Create Asymmetric Objects

Once you have an open Realm, you can create an asymmetric object inside a
write transaction using Realm.create(). When creating an asymmetric
object, Realm.create() returns undefined rather than the object itself.

const App = () => {


// Getting access to our opened realm instance
const realm = useRealm();
const handleAddSensor = () => {
realm.write(() => {
realm.create('WeatherSensor', {
_id: weatherSensorPrimaryKey,
deviceId: "WX1278UIT",
temperatureInFahrenheit: 66.7,
barometricPressureInHg: 29.65,
windSpeedInMph: 2,
});
});
}
return (
<Button
title='Add A New Sensor'
onPress={() => handleAddSensor()}
/>
)
};

You cannot read these objects. Once created, they sync to the App Services
backend and the linked Atlas database.

Atlas Device Sync completely manages the lifecycle of this data. It is


maintained on the device until Data Ingest synchronization is complete, and
then removed from the device.

Testing - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can test the Realm React Native SDK with popular React Native testing
libraries like Jest, Jasmine, and Mocha.

TIP

See also:
Official React Native Testing Documentation

Clean Up Tests

When testing the Realm React Native SDK, you must close realms
with Realm.close() after you're done with them to prevent memory leaks.

You should also delete the realm file with Realm.deleteFile() during clean up to
keep your tests idempotent.

The below example uses the Jest testing framework. It uses Jest's built-
in beforeEach() and afterEach() hooks for test set up and tear down,
respectively.

const config = {
schema: [Car],
};
let realm;
beforeEach(async () => {
realm = await Realm.open(config);
});
afterEach(() => {
if (!realm.isClosed) {
realm.close();
}
if (config) {
Realm.deleteFile(config);
}
});
test("Close a Realm", async () => {
expect(realm.isClosed).toBe(false);
realm.close();
expect(realm.isClosed).toBe(true);
});

Debugging with Flipper - React Native SDK

Realm is now Atlas Device SDK – Learn More


To debug Realm apps built with React Native, use the Flipper Debugger.
Flipper provides a way to visualize, inspect, and control your apps from a
simple desktop interface. In addition to Flipper's intuitive interface, the Realm
Flipper Plugin provides additional ways to inspect your Realm Database
through:

• Live Objects: See objects in real-time.

• Schema Dependency Graph: View schemas and their relationships.

• Make changes: Create, modify and delete objects

• Query: Query the database using RQL

Requirements

• Using the Hermes JS Engine

• Realm JS version 11.0 or greater

Setup
1

Install the Flipper Desktop App

Follow the Getting Started with Flipper guide to install Flipper for your
operating system.

Add Flipper to your React Native App

If you did not use react-native init to initialize your app, follow the
manual setup guide(s) to add Flipper to your app:

• Manual Android Setup

• Manual iOS Setup


If you did use react-native init to initialize your app and are on React
Native version 0.62 or greater, Flipper integration is supported out of the box.
Otherwise, if you are on an earlier version of React Native, follow
the Automatic Setup guide.

Launch the Flipper Desktop App

Launch your React Native mobile app and open the Flipper Desktop
application. Your React Native app should be available in Flipper's App
Inspect tab.

Add the Realm Flipper Plugin to the Flipper Desktop App

Within the Flipper Desktop App, click the Plugin Manager tab on the left-hand
side. When the Plugin Manager modal opens, select the Install Plugins option.
Search for the "realm-flipper-plugin" and install it.

Then, click the red alert to reload the Flipper Desktop app and apply changes.

Install Realm Flipper Plugin Device with NPM

Realm Flipper Plugin Device enables React Native applications to interact with
Realm Flipper Plugin. Realm Flipper Plugin Device also requires the React
Native Flipper plugin. To install both, run the following command:

npm install realm-flipper-plugin-device react-native-flipper

If you are developing on iOS, run the following command to auto-link


the react-native-flipper module with CocoaPods:

cd ios
pod install
cd ..
6

Import Realm Flipper Plugin in your Code


Import the RealmPlugin component realm-flipper-plugin-
device package in the file in which you open a realm.

import RealmPlugin from 'realm-flipper-plugin-device';

Within your component's return statement, render your RealmPlugin and


pass it the opened realm as a parameter. You can pass in multiple opened
realms.

return (
<View>
<RealmPlugin realms={[realm]} /> {/* pass in the Realm to the plugin*/}
// ...
</View>
);

Navigate back to the Realm Flipper Desktop app. Under the Enabled plugins,
you should see the Realm plugin.

If you don't see the Realm plugin enabled, click the Disabled dropdown and
enable the Realm Flipper Plugin by tapping the + Enable Plugin button.

Under Enabled, you should see the Realm plugin.

Using the Realm Flipper plugin, you can now:

• create, modify and delete your realm objects

• query the database using RQL

• view schemas and their relationships using the Schema Dependency


Graph

Logging - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can set or change your app's log level to develop or debug your
application. You might want to change the log level to log different amounts of
data depending on the app's environment. You can specify different log levels
or custom loggers.

TIP

See also:

This page shows how to set a Realm logger, which was added in Realm React
Native SDK v12.0.0. This supersedes setting the Sync client log level in earlier
versions of the Realm React Native SDK. For information on how to set the
Sync client log level in an earlier version, refer to Set the Client Log Level -
React Native SDK.

Set or Change the Realm Log Level

You can set the level of detail reported by the Realm React Native SDK. To
configure the log level, pass a valid level string value to setLogLevel():

• "all"

• "trace"

• "debug"

• "detail"

• "info"

• "warn"

• "error"

• "fatal"

• "off"
Realm.setLogLevel('trace');

Use setLogLevel() anywhere in your app to increase or decrease log


verbosity at different points in your code. This behavior differs from the
deprecated sync client log level, which had to be set before opening a synced
realm and could not be changed.
To turn off logging, pass "off" to setLogLevel():

Realm.setLogLevel('off');

Customize the Logger

To set a custom logger, call setLogger(). This method


recieves level and message arguments from the Realm logger, not individual
realms. Use these arguments to define your own logging behavior.

You must use setLogger() before you open a realm with RealmProvider.
You can't use setLogger() in a hook in the same component
as RealmProvider because RealmProvider opens a realm when it is
mounted. Hooks generally run after a component is mounted, which
means RealmProvider already opened a realm.

Most of the time, you should set your custom logger outside of the React tree.
For example, in your app's root index.js file.

Realm.setLogger((level, message) => {


const log = {
message,
level,
};
setLogs(previousLogs => [...previousLogs, log]);
});

This sets the logging behavior for all Realm logging in your application,
regardless of where you set it. If you do not provide a log level, the default is
"info".

Telemetry - React Native SDK

Realm is now Atlas Device SDK – Learn More

MongoDB collects anonymized telemetry data from the Realm SDKs to better
understand how and where developers use Realm. This data helps us
determine what to work on next and lets us gracefully deprecate features with
minimal impact. None of the telemetry data collected by the Realm SDKs
personally identifies you, your app, or your employer.

Data is collected whenever you install the SDK, build your app (if applicable),
or run your app in a non-production, debugging environment.

MongoDB collects the following information:

• An anonymized machine ID and bundle ID

• The Realm SDK version

• Your programming language and framework versions

• Your operating system platform and version

Telemetry is on by default for the Realm SDKs. You can disable telemetry at
any time b by setting the REALM_DISABLE_ANALYTICS environment variable
to true in your shell environment:

export
REALM_DISABLE_ANALYTICS=true

Sync Data Between Devices - React Native SDK

Realm is now Atlas Device SDK – Learn More

Atlas Device Sync automatically synchronizes data between client applications


and an Atlas App Services backend. When a client device is online, Sync
asynchronously synchronizes data in a background thread between the device
and your backend App.

TIP

See also:

Configure and Enable Atlas Device Sync


Flexible Sync

When you select Flexible Sync for your backend App configuration, your client
implementation must include subscriptions to queries on queryable fields.
Flexible Sync works by synchronizing data that matches query subscriptions
you maintain in the client application.

A subscription set contains a set of queries. Realm Flexible Sync returns


documents matching those queries, where the user has the
appropriate permissions to read and/or read and write the documents. If
documents match the query, but the client does not have the permission to
read or write them, they do not sync to the client application.

You can form queries using Realm Query Language.

NOTE

Flexible Sync does not support all the query operators available in Realm
Query Language and the SDK's query engine. See Flexible Sync RQL
Limitations for details.

Subscription sets are based on a specific type of Realm object. You might
have multiple subscriptions if you have many types of Realm objects.

To use Flexible Sync in your client application, open a synced realm with a
Flexible Sync configuration. Then, manage subscriptions to determine which
documents to sync.

TIP

See also:

• Learn how to configure and open a realm using Flexible Sync.

• Learn how to Manage Flexible Sync Subscriptions

Group Updates for Improved Performance


Every write transaction for a subscription set has a performance cost. If you
need to make multiple updates to a Realm object during a session, consider
keeping edited objects in memory until all changes are complete. This
improves sync performance by only writing the complete and updated object
to your realm instead of every change.

TIP

Device Sync supports two Sync Modes: Flexible Sync, and the older Partition-
Based Sync. If your App Services backend uses Partition-Based Sync, refer
to Partition-Based Sync - React Native SDK.

We recommend new apps use Flexible Sync.

Configure a Synced Realm - React Native SDK

Realm is now Atlas Device SDK – Learn More

You can configure a realm to automatically synchronize data between many


devices that each have their own local copy of the data.

For more information about synced realms, including directions on how to set
up sync in an App Services App, refer to Atlas Device Sync Overview.

For more information about different realm configurations, refer to Configure a


Realm.

Prerequisites

Before you configure a realm with Flexible Sync in a React Native application:

1. Enable Flexible Sync on the backend. You must configure Flexible Sync
in the backend before you can use it with your client application.

2. Initialize the App client.


3. Authenticate a user in your client project.

Configure a Synced Realm

Configure a synced realm using the providers from @realm/react.

By default, Realm syncs all data from the server before returning anything. If
you want to sync data in the background, read Configure a Synced Realm
While Offline.

To configure a synced realm:

1. Import providers from @realm/react.

2. Configure AppProvider.

3. Configure UserProvider and nest it within AppProvider.

4. Configure RealmProvider for sync and nest it within UserProvider.


You must set up a sync subscription. The example below uses an initial
subscription, but you can also set up subscriptions
in RealmProvider child components.

This is how you nest providers:

import React from 'react';


import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
schema={[YourObjectModel]}
sync={{
flexible: true,
initialSubscriptions: {
update(subs, realm) {
subs.add(realm.objects(YourObjectModel));
},
},
}}>
<RestOfApp />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

NOTE

Partition-Based Sync

This page covers Flexible Sync realms. Flexible Sync is the preferred mode for
new apps that use Atlas Device Sync. For information about realms using the
older Partition-Based Sync, refer to Partition-Based Sync.

AppProvider

The @realm/react AppProvider gives you access to an instance of your App


Services App.

To set up your App client, pass the App ID string to the id prop
of AppProvider. Wrap any components that need to access the App with
the AppProvider.

import React from 'react';


import {AppProvider} from '@realm/react';
function AppWrapper() {
return (
<View>
<AppProvider id={APP_ID}>
<MyApp />
</AppProvider>
</View>
);
}

You can find more information about AppProvider on the Connect To Atlas
App Services page.

UserProvider

UserProvider gives you access to a Realm user. A UserProvider is required


for an app to use the hooks.

First, you need to configure user authentication


with AppProvider and UserProvider. Then, work with authentication using
the useApp() and useUser() hooks.

To set up user authentication:

1. Wrap all components you want to use with App Services in


an AppProvider.

2. Inside of AppProvider, wrap all components that need access to an


authenticated user with a UserProvider.

3. In UserProvider, include a fallback prop with another component


that logs a user in. The app renders this component if there is no
authenticated user.
4. In the component passed to the UserProvider.fallback prop,
authenticate a user with Realm.App.logIn(), which you can access
with the useApp() hook.

Components wrapped by UserProvider only render if your app has an


authenticated user. These components can access the authenticated user with
the useUser() hook.

import React from 'react';


import {useApp, UserProvider, AppProvider} from '@realm/react';
import {Button} from 'react-native';
function AppWrapper() {
return (
<AppProvider id={APP_ID}>
{/* If there is no authenticated user,
the app mounts the `fallback` component.
Once the user successfully authenticates,
the app unmounts the component in the
`UserProvider.fallback` prop
(the `LogIn` component in this example). */}
<UserProvider fallback={LogIn}>
{/* Components with access to the user.
These components only mount
if there's an authenticated user.*/}
<RestOfApp />
</UserProvider>
</AppProvider>
);
}
function LogIn() {
const app = useApp();
// This example uses anonymous authentication.
// However, you can use any authentication provider
// to log a user in with this pattern.
async function logInUser() {
await app.logIn(Realm.Credentials.anonymous());
}
return (
<Button
title='Log In'
onPress={logInUser}
/>
);
}

You can find more information about UserProvider on the Authenticate


Users page.

RealmProvider

RealmProvider is a wrapper that exposes a realm to its child components. You


configure your realm by passing props to RealmProvider.

When RealmProvider is rendered, it opens the realm. This means that the
provider renders successfully or its child components can't access the realm.

To configure a synced realm:

1. Import providers from @realm/react.

2. Configure AppProvider.

3. Configure UserProvider and nest it within AppProvider.

4. Pass your object models to RealmProvider's schema prop.


5. Create a FlexibleSyncConfiguration object.

6. Pass your SyncConfiguration object to the sync prop or add the


object in-line.

7. Set up initial subscriptions or create a new subscription


in RealmProvider child components.

8. Add other Configuration object properties as props


to RealmProvider to further configure your realm.
import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>

<RealmProvider
schema={[YourObjectModel]}
sync={{
flexible: true,
initialSubscriptions: {
update(subs, realm) {
subs.add(realm.objects(YourObjectModel));
},
},
}}>
<RestOfApp />
</RealmProvider>

</UserProvider>
</AppProvider>
);
}
For more information about configuring and using RealmProvider, check out
the Configure a Realm page.

Configuration Options

You can configure RealmProvider by setting props that match the properties
of a Configuration object. You can also set fallback and realmRef props.

• realmRef
Used with useRef to expose the configured realm to processes outside
of RealmProvider. This can be useful for things like a client reset fallback.

• fallback
Rendered while waiting for the realm to open. Local realms usually open fast
enough that the fallback prop isn't needed.

Open Synced Realm at Specific Path

New in version [email protected] .

Using AppConfiguration.baseFilePath, and Realm.BaseConfiguration.path, you


can control where Realm and metadata files are stored on client devices.

To do so, set AppProvider.baseFilePath. If baseFilePath is not set, the


current work directory is used. You can also
set RealmProvider.sync.path for more control.

TypeScript
JavaScript
import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperSync({customBaseFilePath}) {
return (

<AppProvider id={APP_ID} baseFilePath={customBaseFilePath}>

<UserProvider fallback={LogIn}>
<RealmProvider

path={customRealmPath}

schema={[Profile]}
sync={{
flexible: true,
}}>
<RestOfApp />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

If baseFilePath is set, metadata is always stored


in <baseFilePath>/mongodb-realm/. If baseFilePath isn't set, then
metadata is stored in <Realm.defaultPath>/mongodb-realm.

Where, exactly, your Realm file is stored can vary depending on how you
set Realm.BaseConfiguration.path:

• Realm.Configuration.path is not set and baseFilePath is set. Your


Realm file is stored at baseFilePath.

• Realm.Configuation.path is set to a relative path. Your Realm file is


stored relative to baseFilePath.

• Realm.Configuration.path is an absolute path. Your Realm file is


stored at Realm.Configuration.path.

Access a Synced Realm While Offline

The following subsections show how to use background synchronization to


access a realm while offline. To do this, use a cached user and
an OpenRealmBehaviorConfiguration object.
Within RealmProvider's sync configuration, set the
optional newRealmFileBehavior and existingRealmFileBehavior fields
to your OpenRealmBehaviorConfiguration object to enable background
synchronization.

You can open a realm immediately with background sync or after a timeout.

NOTE

Initial login requires a network connection

When a user signs up for your app, or logs in for the first time with an existing
account on a client, the client must have a network connection. Checking for
cached user credentials lets you open a realm offline, but only if the user has
previously logged in while online.

Access Immediately with Background Sync

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.

TypeScript
JavaScript
import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperOfflineSync() {
const realmAccessBehavior = {
type: 'openImmediately',
};
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
schema={[Profile]}
sync={{
flexible: true,
newRealmFileBehavior: realmAccessBehavior,
existingRealmFileBehavior: realmAccessBehavior,
}}>
<RestOfApp />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

Access After Timeout with Background Sync

If you want to sync data but you're in an environment where it's uncertain if the
user has an Internet connection, specify a timeOut. This automatically opens
the realm when either:

• the timeout period elapses.

• the realm has completely downloaded.

If the realm doesn't finish downloading before the timeout, the initial Sync
continues in the background.

TypeScript
JavaScript
import React from 'react';
import {AppProvider, UserProvider, RealmProvider} from '@realm/react';
function AppWrapperTimeoutSync() {
const realmAccessBehavior = {
type: 'downloadBeforeOpen',
timeOutBehavior: 'openLocalRealm',
timeOut: 1000,
};
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
schema={[Profile]}
sync={{
flexible: true,
newRealmFileBehavior: realmAccessBehavior,
existingRealmFileBehavior: realmAccessBehavior,
}}>
<RestOfApp />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}

@realm/react Providers and Hooks

@realm/react has providers and hooks that simplify working with your
synced realm and its data.

AppProvider
Provider/Hook Description Example

AppProvider React component that provides an See AppProvider


App Services App instance for the
Sync hooks. An AppProvider is
required for a client to use Sync
hooks.
useApp Accesses the current App Services const app = useApp();
App instance from
the AppProvider context.

useAuth Provides methods and state for const auth = useAuth();


authenticating with an App
Services App.
useEmailPasswordAuthProvides methods and state for const auth = useEmailPasswordA
authenticating with an App
Services App using email and
password authentication. It also
contains utility methods, like
resetting a password and
confirming a user.

UserProvider

Provider/Hook Description Example

UserProvider React component that provides a Realm user for the See UserProvider
Sync hooks. A UserProvider is required for an app to use
Sync hooks.
useUser Accesses the currently-authenticated Realm user from const user = useUse
the UserProvider context. The user is stored as React
state and will trigger a re-render whenever it changes.

RealmProvider
Provider/Hook Description Example

RealmProviderA wrapper that exposes a realm to its child See RealmProvider


components, which have access to hooks
that let you read, write, and update data.
useRealm Returns the instance of the Realm opened const realm = useRealm();
by the RealmProvider.
useObject Returns an object (Realm.Object<T>) from const myTask = useObject(Task,
a given type and value of primary key.
Updates on any changes to the returned
object. Returns null if the object either
doesn't exists or has been deleted.
useQuery Returns a collection of objects const tasks = useQuery(Task);
(Realm.Results<T & Realm.Object T>)
from a given type. Updates on any changes
to any object in the collection. Returns an
empty array if the collection is empty.

Manage Sync Subscriptions - React Native SDK

Realm is now Atlas Device SDK – Learn More

Flexible Sync uses subscriptions and permissions to determine what data to


sync with your App. You must have at least one subscription before you can
read from or write to a realm with Flexible Sync enabled.
The @realm/react library streamlines permissions and queries for sync
subscriptions.

You can add, update, and remove query subscriptions to control what data
syncs to the client device. In the Realm.js v12.0.0 and later, you can subscribe
to queries instead of or in addition to manually managing subscriptions.

You can't create subscriptions for Data Ingest and asymmetric


objects because they only send data to your App Services backend.

IMPORTANT
Flexible Sync Query Limitations

Flexible Sync subscriptions only support a subset of the RQL query operators.
Refer to the Flexible Sync RQL Limitations documentation for information on
which operators are not supported.

Prerequisites

You need to meet the following requirements before you can use Atlas Device
Sync with the React Native SDK:

• A non-sharded Atlas cluster running MongoDB 5.0 or later.

• Realm.js v10.12.0 or later.

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

1. Configure Flexible Sync on the backend

2. Initialize the app client

3. Authenticate a user in the client

4. Configure a synced Realm

Align Subscriptions with Backend App

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.

Subscribe to Queries

New in version [email protected] .

[email protected] adds experimental APIs that subscribe to and unsubscribe


from a query's results. These APIs abstract away the details of manually
adding and removing subscriptions.

For all subscriptions, you need an authenticated user and a Flexible Sync
realm.

Subscribe to a Query

We recommend that you name your subscriptions. This makes finding and
managing your subscriptions easier. Subscription names must be unique.
Trying to add a subscription with the same name as an existing subscription
overwrites the existing subscription.

To subscribe to a query:

1. Query for the objects that you want to read and write.

2. Call subscribe() on the query results to create a sync subscription for


objects matching the query.

3. Pass a SubscriptionOptions object that contains the name property


to subscribe().
import React, {useEffect, useState} from 'react';
import {useRealm, useQuery} from '@realm/react';
import {View, Text, FlatList} from 'react-native';
import {Bird} from './models/Bird';
import {Subscription} from 'realm/dist/bundle';
export const BasicSubscription = () => {
const realm = useRealm();
// Get all local birds that have not been seen yet.
const seenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == true'),
);
const [seenBirdsSubscription, setSeenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
// Create an async function so that we can `await` the
// promise from `.subscribe()`.
const createSubscription = async () => {
await seenBirds.subscribe({
name: 'Birds I have seen',
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('Birds I have seen');
// ... and set it to a stateful variable or manage it in `useEffect`.
setSeenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

Most of the time, you should give your subscriptions a name. If you don't, the
name is set to null.

If you use filtered() on an unnamed query subscription, the subscription


identifier is based on the filtered query. This means that every time your
query string changes, subscribe() will create a new subscription.

API Reference

• subscribe()

• SubscriptionOptions

• null

Wait for a Query Subscription to Sync

When you subscribe to a query's results, the results do not contain objects
until synced data is downloaded. When you do need to wait for synced objects
to finish downloading, configure the waitForSync option. You can specify
different behavior for your subscriptions and how they handle writing for
downloads.

This example uses the FirstTime option, which is the default behavior. A
subscription with FirstTime behavior only waits for sync to finish when a
subscription is first created.

import React, {useEffect, useState} from 'react';


import {BSON, WaitForSync} from 'realm';
import {useRealm, useQuery} from '@realm/react';
import {View, Text, Button, TextInput, FlatList} from 'react-native';
import {Bird} from './models/Bird';
import {Subscription} from 'realm/dist/bundle';
export const WaitFirstTime = () => {
const realm = useRealm();
const [birdName, setBirdName] = useState('Change me!');
// Get local birds that have been marked as "haveSeen".
const seenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == true'),
);
const [seenBirdsSubscription, setSeenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
const createSubscription = async () => {
// Only wait for sync to finish on the initial sync.
await seenBirds.subscribe({
behavior: WaitForSync.FirstTime,
name: 'First time sync only',
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('First time sync only');
// ... and set it to a stateful variable or manage it in `useEffect`.
setSeenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

The other supported WaitForSync options are:


• Always: Wait to download matching objects every time your app
launches. The app must have an internet connection at every launch.

• Never: Never wait to download matching objects. The app needs an


internet connection for the user to authenticate the first time the app
launches, but can open offline on subsequent launches using cached
credentials.

You can optionally specify a timeout value to limit how long the sync
download runs:

export const AlwaysWait = () => {


const realm = useRealm();
// Get all local birds that have not been seen yet.
const unSeenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == false'),
);
const [unSeenBirdsSubscription, setUnseenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
const createSubscription = async () => {
// Add subscription with timeout.
// If timeout expires before sync is completed, currently-downloaded
// objects are returned and sync download continues in the background.
await unSeenBirds.subscribe({
behavior: WaitForSync.Always,
name: 'Always wait',
timeout: 500,
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('Always wait');
// ... and set it to a stateful variable or manage it in `useEffect`.
setUnseenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

API Reference

• waitForSync
Unsubscribe from a Query

Subscriptions persist across user sessions unless you unsubscribe from


them. You can unsubscribe from a query's results using unsubscribe().

This removes the subscription from the list of active subscriptions, similar
to manually removing a subscription.

A results list may still contain objects after calling unsubscribe() if another
subscription exists that contains overlapping objects.

When you call unsubscribe(), the associated subscription is removed.


Subscriptions are removed by name. If they don't have a
name, unsubscribe() removes any queries that exactly match the one you
call unsubscribe() on.

The unsubscribe() method returns before objects matching the removed


subscription are deleted from the realm. Sync continues in the background
based on the new set of subscriptions.

import React, {useEffect, useState} from 'react';


import {useRealm, useQuery} from '@realm/react';
import {View, Text, Button} from 'react-native';
import {Bird} from './models/Bird';
import {Subscription} from 'realm/dist/bundle';
export const Unsubscribe = () => {
const realm = useRealm();
const birds = useQuery(Bird);
const unsubscribeFromQuery = () => {
birds.unsubscribe();
};
return (
<View>
<Button
title="Unsubscribe"
onPress={() => {
unsubscribeFromQuery();
}}
/>
</View>
);
};
API Reference

• unsubscribe()

Manually Manage Subscriptions

You can use the Subscriptions API to manually manage a set of subscriptions
to specific queries on queryable fields.

You can use the Realm.subscriptions API 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() hook gives you access
to the currently-opened realm.

You can do the following with your subscriptions:

• Add subscriptions

• Configure a realm with initial subscriptions

• Get a list of all subscriptions

• Check the status of subscriptions

• Remove 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.

When you create a subscription, Realm looks for data matching a query on a
specific object type. You can have subscriptions on several different object
types. You can also have multiple queries on the same object type.

IMPORTANT

Object Links
You must add both an object and its linked object to the subscription set to
see a linked object.

If your subscription results contain an object with a property that links to an


object not contained in the results, the link appears to be null. There is no way
to distinguish whether that property's value is legitimately null, or whether the
object it links to exists but is out of view of the query subscription.

Access All Subscriptions

Within a RealmProvider configured for Flexible Sync, you can access


a SubscriptionSet. A SubscriptionSet is a collection of all subscriptions for
your app.

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>}
/>
);
}

API Reference
• SubscriptionSet

Add a Subscription

In the following example, completed and progressMinutes have been set as


queryable fields in an App Services App. In the client code, we create filtered
queries and then subscribe to their results:

• Completed tasks

• Completed tasks that have taken over 120 progressMinutes

Note that 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>}
/>
);
}
Configure a Realm with Initial Subscriptions

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 a RealmProvider's sync
configuration.

You can't use the @realm/react library


hooks useQuery and useObject when setting initial subscriptions. Instead,
use the Realm.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>}
/>
);
}

By default, initial subscriptions are only created the first time a realm is
opened. If your app needs to rerun this initial subscription every time the app
starts, you can set rerunOnOpen to true. You might need to do this to rerun
dynamic time ranges or other queries that require a recomputation of static
variables for the subscription.

API Reference

• FlexibleSyncConfiguration

Check the Status of Subscriptions

You can check the subscription state to see if the server has acknowledged
the subscription 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>
);
}

New in version [email protected] .

Realm.js v12.0.0 added the SubscriptionSetState enum that you can use to get
the status of a subscription.

Subscription State "Complete"

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.

Update Subscriptions with a New Query


You can update a named subscription with a new query. To update a
subscription's query, pass the new query and a subscription option with the
name of the subscription that you want to update to
the MutableSubscriptionSet.add() method. Like adding a new
subscription, you must update a subscription within a transaction by
calling subscriptions.update().

The following example, redefines long-running tasks as any tasks that take
more than 180 minutes.

realm.subscriptions.update((mutableSubs) => {
mutableSubs.add(
tasks.filtered('status == "completed" && progressMinutes > 180'),
{
name: "longRunningTasksSubscription",
}
);
});

NOTE

Attempting to update a subscription that has


the SubscriptionOptions.throwOnUpdate field set to true throws an
exception.

API Reference

• MutableSubscriptionSet.add()

• SubScriptionSet.update()

• SubscriptionOptions.throwOnUpdate

Remove Subscriptions

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.
You can remove subscriptions in the following ways:

• Remove a single subscription with a specific query

• Remove a single subscription with a specific name

• Remove all subscriptions to a specific object model

• Remove all unnamed subscriptions

• Remove all subscriptions

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

Examples in this section 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 (
// ...
);
}

Remove a Subscription by Query

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"'));
});

Remove a Subscription by Name

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");
});

Remove a Subscription by Reference

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);

Remove All Subscriptions on an Object Type

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");
});

Remove All Unnamed Subscriptions

New in version [email protected] .


You may want to remove unnamed subscriptions that are transient or
dynamically generated, but leave named subscriptions in place.

You can remove all unnamed subscriptions from the subscription set by
calling .removeUnnamed() on mutableSubs. .removeUnnamed() returns the
number of unnamed subscriptions removed.

// Remove unnamed subscriptions.


let numberRemovedSubscriptions = 0;
await realm.subscriptions.update((mutableSubs) => {
numberRemovedSubscriptions = mutableSubs.removeUnnamed();
});

API Reference

• removeUnnamed()

Remove All Subscriptions

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();
});

Performance Considerations

API Efficiency

Managing multiple subscriptions with


the subscribe() and unsubscribe() APIs described in the Subscribe to
Queries section is less efficient than performing batch updates when you
manually manage subscriptions.
For better performance when making multiple subscription changes, use
the subscriptions API to update all the subscriptions in a single transaction.
To learn how, see Manually Manage Subscriptions.

Group Updates for Improved Performance

Every write transaction for a subscription set has a performance cost. If you
need to make multiple updates to a Realm object during a session, consider
keeping edited objects in memory until all changes are complete. This
improves sync performance by only writing the complete and updated object
to your realm instead of every change.

Flexible Sync RQL Requirements and Limitations

Indexed Queryable Fields Subscription Requirements

Adding an indexed queryable field to your App can improve performance for
simple queries on data that is strongly partitioned. For example, an app where
queries strongly map data to a device, store, or user, such as user_id ==
$0, “641374b03725038381d2e1fb”, is a good candidate for an indexed
queryable field. However, an indexed queryable field has specific requirements
for use in a query subscription:

• The indexed queryable field must be used in every subscription query. It


cannot be missing from the query.

• The indexed queryable field must use an == or IN comparison against a


constant at least once in the subscription query. For example, user_id
== $0, "641374b03725038381d2e1fb" or store_id IN $0,
{1,2,3}.

You can optionally include an AND comparison as long as the indexed


queryable field is directly compared against a constant using == or IN at least
once. For example, store_id IN {1,2,3} AND
region=="Northeast" or store_id == 1 AND (active_promotions < 5
OR num_employees < 10).

Invalid Flexible Sync queries on an indexed queryable field include queries


where:

• The indexed queryable field does not use AND with the rest of the query.
For example store_id IN {1,2,3} OR region=="Northeast" is
invalid because it uses OR instead of AND. Similarly, store_id == 1
AND active_promotions < 5 OR num_employees < 10 is invalid
because the AND only applies to the term next to it, not the entire query.

• The indexed queryable field is not used in an equality operator. For


example store_id > 2 AND region=="Northeast" is invalid
because it uses only the > operator with the indexed queryable field and
does not have an equality comparison.

• The query is missing the indexed queryable field entirely. For


example, region=="Northeast or truepredicate are invalid because
they do not contain the indexed queryable field.

Unsupported Query Operators in Flexible Sync

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

@avg, @count, @max, @min, @sum


Aggregate Operators

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.

List Queries

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"

Embedded or Linked Objects

Flexible Sync does not support querying on properties in Embedded Objects


or links. For example, obj1.field == "foo".

Manual Client Reset Data Recovery - React Native SDK


Realm is now Atlas Device SDK – Learn More

This page explains how to manually recover unsynced realm data after a client
reset using the Manual Recovery client reset mode.

Manual recovery requires significant amounts of code, schema concessions,


and custom conflict resolution logic. You should only perform a manual
recovery of unsynced realm data if you cannot lose unsynced data and the
other automatic client reset methods do not meet your use case.

For more information about the other available client reset modes, refer
to Reset a Client Realm.

The specifics of manual recovery depend heavily upon your application and
your schema. However, there are a few techniques that can help with manual
recoveries. The Track Changes by Object Strategy section demonstrates one
method of recovering unsynced changes during a client reset.

WARNING

Avoid Making Breaking Schema Changes in Production

Do not expect to recover all unsynced data after a breaking schema change.
The best way to preserve user data is to never make a breaking schema
change at all.

IMPORTANT

Breaking Schema Changes Require an App Schema Update

After a breaking schema change:

• All clients must perform a client reset.

• You must update client models affected by the breaking schema


change.

Track Changes by Object Strategy


The track changes by object manual client reset data recovery strategy lets
you recover data already written to the client realm file but not yet synced to
the backend.

In this strategy, you add a "Last Updated Time" to each object model to track
when each object last changed. We'll watch the to determine when the realm
last uploaded its state to the backend.

When backend invokes a client reset, find objects that were deleted, created,
or updated since the last sync with the backend. Then copy that data from the
backup realm to the new realm.

The following steps demonstrate implementing the process at a high level:

1. Client reset error: Your application receives a client reset error code
from the backend.

2. Strategy implementation: The SDK calls your strategy implementation.

3. Close all instances of the realm: Close all open instances of the realm
experiencing the client reset. If your application architecture makes this
difficult (for instance, if your app uses many realm instances
simultaneously in listeners throughout the application), it may be easier
to restart the application. You can do this programmatically or through a
direct request to the user in a dialog.

4. Move the realm to a backup file: Call


the Realm.App.Sync.initiateClientReset() static method. This method
moves the current copy of the client realm file to a backup file.

5. Open new instance of the realm: Open a new instance of the realm using
your typical sync configuration. If your application uses multiple realms,
you can identify the realm experiencing a client reset from the backup
file name.

6. Download all realm data from the backend: Download the entire set of
data in the realm before you proceed. This is the default behavior of
the FlexibleSyncConfiguration object.
7. Open the realm backup: Use the error.config object passed as an
argument to the SyncConfiguration.error callback function.

8. Migrate unsynced changes: Query the backup realm for data to recover.
Insert, delete or update data in the new realm accordingly.

Example

This example demonstrates implementing the track changes by object manual


client reset data recovery strategy.

NOTE

Limitations of This Example

• This example only is for an application with a single realm containing a


single Realm object type. For each additional object type, you'd need to
add another sync listener as described in the Track Synchronization in
Separate Realm section.

• This example keeps track of the last time each object was updated. As a
result, the recovery operation overwrites the entire object in the new
realm if any field was updated after the last successful sync of the
backup realm. This could overwrite fields updated by other clients with
old data from this client. If your realm objects contain multiple fields
containing important data, consider keeping track of the last updated
time of each field instead, and recovering each field individually.

For more information on other ways to perform a manual client reset with data
recovery, refer to the Alternative Strategies section.
1

Include Last Updated Time in Your Schema

Add a new property to your Realm object schema to track the last time it was
updated. Whenever you create or update a Realm object with the schema,
include a timestamp with the update time.
Ordinarily, there is no way to detect when a Realm object was last modified.
This makes it difficult to determine which changes were synced to the
backend. By adding a timestamp lastUpdated to your Realm object models
and updating that timestamp to the current time whenever a change occurs,
you can keep track of when objects were changed.

const DogSchema = {
name: "Dog",
properties: {
name: "string",
age: "int?",
lastUpdated: "int",
},
};
2

Configure Realm to Use Manual Client Reset

In the realm's FlexibleSyncConfiguration, set the clientReset field to manual


mode and include an error callback function. You'll define the error callback
function in the Create Callback to Handle Client Reset section.

const config = {
schema: [DogSchema],
sync: {
user: app.currentUser,
partitionValue: "MyPartitionValue",
clientReset: {
mode: "manual",
},
error: handleSyncError, // callback function defined later
},
};
3

Track Synchronization in Separate Realm

Just knowing when objects were changed isn't enough to recover data during
a client reset. You also need to know when the realm last completed a sync
successfully. This example implementation uses a singleton object in a
separate realm called LastSynced paired with a change listener to record
when a realm finishes syncing successfully.
Define your LastSynced Realm to track the latest time your realm
synchronizes.

const LastSyncedSchema = {
name: "LastSynced",
properties: {
realmTracked: "string",
timestamp: "int?",
},
primaryKey: "realmTracked",
};
const lastSyncedConfig = { schema: [LastSyncedSchema] };
const lastSyncedRealm = await Realm.open(lastSyncedConfig);
lastSyncedRealm.write(() => {
lastSyncedRealm.create("LastSynced", {
realmTracked: "Dog",
});
});

Register a change listener to subscribe to changes to the Dog collection. Only


update the LastSynced object if the sync session is connected and all local
changes have been synced with the server.

// Listens for changes to the Dogs collection


realm.objects("Dog").addListener(async () => {
// only update LastSynced if sync session is connected
// and all local changes are synced
if (realm.syncSession.isConnected()) {
await realm.syncSession.uploadAllLocalChanges();
lastSyncedRealm.write(() => {
lastSyncedRealm.create("LastSynced", {
realmTracked: "Dog",
timestamp: Date.now(),
});
});
}
});
4

Create Callback to Handle Client Reset

Now that you've recorded update times for all objects in your application as
well as the last time your application completed a sync, it's time to implement
the manual recovery process. This example handles two main recovery
operations:
• Restore unsynced inserts and updates from the backup realm

• Delete objects from the new realm that were previously deleted from the
backup realm

You can follow along with the implementation of these operations in the code
samples below.

async function handleSyncError(_session, error) {


if (error.name === "ClientReset") {
const realmPath = realm.path; // realm.path will not be accessible after realm.close()
realm.close(); // you must close all realms before proceeding
// pass your realm app instance and realm path to initiateClientReset()
Realm.App.Sync.initiateClientReset(app, realmPath);
// Redownload the realm
realm = await Realm.open(config);
const oldRealm = await Realm.open(error.config);
const lastSyncedTime = lastSyncedRealm.objectForPrimaryKey(
"LastSynced",
"Dog"
).timestamp;
const unsyncedDogs = oldRealm
.objects("Dog")
.filtered(`lastUpdated > ${lastSyncedTime}`);
// add unsynced dogs to synced realm
realm.write(() => {
unsyncedDogs.forEach((dog) => {
realm.create("Dog", dog, "modified");
});
});
// delete dogs from synced realm that were deleted locally
const syncedDogs = realm
.objects("Dog")
.filtered(`lastUpdated <= ${lastSyncedTime}`);
realm.write(() => {
syncedDogs.forEach((dog) => {
if (!oldRealm.objectForPrimaryKey("Dog", dog._id)) {
realm.delete(dog);
}
});
});
// make sure everything syncs and close old realm
await realm.syncSession.uploadAllLocalChanges();
oldRealm.close();
} else {
console.log(`Received error ${error.message}`);
}
}
Alternative Strategies

Possible alternate implementations include:

• Overwrite the entire backend with the backup state: With no "last
updated time" or "last synced time", upsert all objects from the backup
realm into the new realm. There is no way to recovered unsynced
deletions with this approach. This approach overwrites all data written
to the backend by other clients since the last sync. Recommended for
applications where only one user writes to each realm.

• Track changes by field: Instead of tracking a "last updated time" for


every object, track the "last updated time" for every field. Update fields
individually using this logic to avoid overwriting field writes from other
clients with old data. Recommended for applications with many fields
per-object where conflicts must be resolved at the field level.

• Track updates separately from objects: Instead of tracking a "last


updated time" in the schema of each object, create another model in
your schema called Updates. Every time any field in any object
(besides Updates) updates, record the primary key, field, and time of
the update. During a client reset, "re-write" all of the Update events that
occurred after the "last synced time" using the latest value of that field
in the backup realm. This approach should replicate all unsynced local
changes in the new realm without overwriting any fields with stale data.
However, storing the collection of updates could become expensive if
your application writes frequently. Recommended for applications
where adding "lastUpdated" fields to object models is undesirable.

Stream Data to Atlas - React Native SDK

Realm is now Atlas Device SDK – Learn More


You can use Data Ingest to stream data from the client application to a Flexible
Sync-enabled Atlas App Services App.

You might want to sync data unidirectionally in IoT applications, such as a


weather sensor sending data to the cloud. Data Ingest is also useful for writing
other types of immutable data where you do not require conflict resolution,
such as creating invoices from a retail app or logging application events.

Data Ingest is optimized to provide performance improvements for heavy


client-side insert-only workloads.

Sync Data Unidirectionally from a Client Application


1

Define an Asymmetric Object

Asymmetric objects sync data unidirectionally. Define an asymmetric object


by setting asymmetric to true in your object model:

TypeScript
JavaScript
class WeatherSensor extends Realm.Object{
static schema = {
name: 'WeatherSensor',
// sync WeatherSensor objects one way from your device
// to your Atlas database.
asymmetric: true,
primaryKey: '_id',
properties: {
_id: 'objectId',
deviceId: 'string',
temperatureInFahrenheit: 'int',
barometricPressureInHg: 'float',
windSpeedInMph: 'float',
},
};
}

For more information on how to define an asymmetric object, refer to Define


an Asymmetric Object.

2
Connect and Authenticate with an App Services App

To stream data from the client to your backend App, you must connect to an
App Services backend and authenticate a user.

function LogIn() {
const app = useApp();
useEffect(() => {
app.logIn(Realm.Credentials.anonymous());
}, []);
return <></>;
}

Data Ingest is a feature of Flexible Sync, so the App you connect to must
use Flexible Sync.

Open a Realm

After you have an authenticated user, you can open a synced realm using a
Flexible Sync configuration object.

TypeScript
JavaScript
// Create a configuration object
const realmConfig = { schema: [WeatherSensor] };
// Create a realm context
const {RealmProvider, useRealm, useObject, useQuery} =
createRealmContext(realmConfig);
// Expose a sync realm
function AppWrapperSync() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
sync={{
flexible: true,
onError: console.error
}}>
<App />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}
Unlike bi-directional Sync, Data Ingest does not use a Flexible Sync
subscription.

You cannot query an asymmetric object or write it to a local realm, so


asymmetric objects are incompatible with bi-directional Flexible Sync,
Partition-Based Sync, or local Realm use.

Create Asymmetric Objects

Once you have an open Realm, you can create an asymmetric object inside a
write transaction using Realm.create(). When creating an asymmetric
object, Realm.create() returns undefined rather than the object itself.

const App = () => {


// Getting access to our opened realm instance
const realm = useRealm();
const handleAddSensor = () => {
realm.write(() => {
realm.create('WeatherSensor', {
_id: weatherSensorPrimaryKey,
deviceId: "WX1278UIT",
temperatureInFahrenheit: 66.7,
barometricPressureInHg: 29.65,
windSpeedInMph: 2,
});
});
}
return (
<Button
title='Add A New Sensor'
onPress={() => handleAddSensor()}
/>
)
};

You cannot read these objects. Once created, they sync to the App Services
backend and the linked Atlas database.

Atlas Device Sync completely manages the lifecycle of this data. It is


maintained on the device until Data Ingest synchronization is complete, and
then removed from the device.

You might also like