Define a Realm Object Model- Node.js SDK
On this page
- Define a Realm Object Type
- Define Realm Object Types with JavaScript Classes
- Supported Property Types
- Define Object Properties
- Specify an Optional Property
- Specify a Primary Key
- Index a Property
- Set a Full-Text Search Index
- Define a Default Property Value
- Map a Property or Class to a Different Name
- Define Relationship Properties
- Define a To-One Relationship Property
- Define a To-Many Relationship Property
- Define an Inverse Relationship Property
- Define an Embedded Object Property
- Define an Asymmetric Object
- Define Unstructured Data
Define a Realm Object Type
To define a Realm object type, create a schema object that specifies the type's
name
and properties
. The type name must be unique among object types in
a realm. For details on how to define specific properties, see Define
Object Properties.
You can define your schemas with JavaScript classes (like most of the examples on this page), but you can also define them as JavaScript objects.
const Car = { name: "Car", properties: { _id: "objectId", make: "string", model: "string", miles: "int?", }, };
Define Realm Object Types with JavaScript Classes
You can define Realm object types with JavaScript classes. To use a class as an
object type, define the object schema on the static property schema
.
class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: { type: "objectId", default: () => new Realm.BSON.ObjectId() }, make: "string", model: "string", miles: "int?", }, primaryKey: "_id", }; }
Note
Class names are limited to a maximum of 57 UTF-8 characters.
Pass the class itself to the schema property of the Realm.Configuration object when opening a realm. You can then read and write data normally.
const realm = await Realm.open({ path: "myrealm", schema: [Car], }); let car1; realm.write(() => { car1 = realm.create(Car, { make: "Nissan", model: "Sentra", miles: 1000, }); });
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.
Realm supports the following primitive data types:
bool
for boolean valuesint
anddouble
, which map to JavaScriptnumber
valuesDecimal128
for high precision numbersstring
date
, which maps to Datedata
, which maps to ArrayBufferobjectId
, which maps to ObjectId
To specify that a field contains a list of a primitive value type, append []
to the type name.
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
.
class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: { type: "objectId", default: () => new Realm.BSON.ObjectId() }, make: "string", model: "string", miles: "int?", }, primaryKey: "_id", }; }
Specify an Optional Property
To mark a property as optional, append a question mark ?
to its type.
The following Car
schema defines an optional miles
property of type int
.
class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: { type: "objectId", default: () => new Realm.BSON.ObjectId() }, make: "string", model: "string", miles: "int?", }, primaryKey: "_id", }; }
Specify 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 efficiently read and modify objects based on their primary key.
If an object type has a primary key, then all objects of that type must include the primary key property with a value that is unique among objects of the same type in a realm. An object type may have at most 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.
The following Car
object schema specifies the _id
property as its
primary key.
class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: { type: "objectId", default: () => new Realm.BSON.ObjectId() }, make: "string", model: "string", miles: "int?", }, primaryKey: "_id", }; }
Index a Property
Realm supports indexing for string, integer, boolean, Date
, UUID
, and ObjectId
properties. To define an index for a given property, set indexed
to
true
.
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.
It's best to only add indexes when optimizing the read performance for specific situations.
The following Car
object schema defines an index on the _id
property.
class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: { type: "objectId", indexed: true }, make: "string", model_name: { type: "string", mapTo: "modelName" }, miles: { type: "int", default: 0 }, }, primaryKey: "_id", }; }
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<Book> { name!: string; price?: number; static schema: ObjectSchema = { name: "Book", properties: { name: { type: "string", indexed: "full-text" }, price: "int?", }, }; }
Define 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.
The following Car
object schema specifies a default value of 0
for
the miles
property:
class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: { type: "objectId", indexed: true }, make: "string", model_name: { type: "string", mapTo: "modelName" }, miles: { type: "int", default: 0 }, }, primaryKey: "_id", }; }
Map a Property or Class to a Different Name
By default, Realm uses the name defined in the model class to represent classes and fields internally. In some cases, you might want to change this behavior. For example:
To make it easier to work across platforms where naming conventions differ. For example, if your Device Sync schema property names use snake case, while your project uses camel case.
To change a class or field name without forcing a migration.
To support multiple model classes with the same name in different packages.
To use a class name that is longer than the 57-character limit enforced by Realm.
You can map a class or property name in your code to a different name to store in a realm. If you write to a synced realm, the Sync schema sees the values stored using the persisted class or property name.
Note that migrations must use the persisted class or property name, and any schema errors reported also use the persisted name.
To use a different class name in your code than is stored in a realm:
Set the
name
property of your Realm object's schema to the name that you want to use to store the object.Use the class name in the Realm configuration's
schema
property when you open the realm.Use the mapped name for performing CRUD operations or when defining Flexible Sync Subscriptions.
In the following example, Realm stores objects created with the
Task
class as Todo_Item
.
class Task extends Realm.Object { static schema = { // Set the schema's `name` property to the name you want to store. // Here, we store items as `Todo_Item` instead of the class's `Task` name. name: "Todo_Item", properties: { _id: "int", name: "string", owner_id: "string?", }, primaryKey: "_id", }; } const config = { // Use the class name in the Configuration's `schema` property when // opening the realm. schema: [Task], sync: { user: anonymousUser, flexible: true, initialSubscriptions: { update: (subs, realm) => { subs.add( realm // Use the mapped name in Flexible Sync subscriptions. .objects(`Todo_Item`) .filtered(`owner_id == "${anonymousUser.id}"`) ); }, }, }, }; const realm = await Realm.open(config); realm.write(() => { // Use the mapped name when performing CRUD operations. realm.create(`Todo_Item`, { _id: 12342245, owner_id: anonymousUser.id, name: "Test the Todo_Item object name", }); }); // Use the mapped name when performing CRUD operations. const assignedTasks = realm.objects(`Todo_Item`);
class Task extends Realm.Object<Task> { _id!: number; name!: string; owner_id?: string; static schema: ObjectSchema = { // Set the schema's `name` property to the name you want to store. // Here, we store items as `Todo_Item` instead of the class's `Task` name. name: "Todo_Item", properties: { _id: "int", name: "string", owner_id: "string?", }, primaryKey: "_id", }; } const config: Realm.Configuration = { // Use the class name in the Configuration's `schema` property when // opening the realm. schema: [Task], sync: { user: anonymousUser, flexible: true, initialSubscriptions: { update: (subs, realm) => { subs.add( realm // Use the mapped name in Flexible Sync subscriptions. .objects(`Todo_Item`) .filtered(`owner_id == "${anonymousUser.id}"`) ); }, }, }, }; const realm = await Realm.open(config); realm.write(() => { // Use the mapped name when performing CRUD operations. realm.create(`Todo_Item`, { _id: 12342245, owner_id: anonymousUser.id, name: "Test the Todo_Item object name", }); }); // Use the mapped name when performing CRUD operations. const assignedTasks = realm.objects(`Todo_Item`);
To use a different property name in your code than is stored in
a realm, set mapTo
to the name of the property as it appears in
your code.
In the following Car
object schema, Realm stores the car's
model name with the snake case model_name
property. The schema maps the property
to modelName
for objects used in client code.
class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: { type: "objectId", indexed: true }, make: "string", model_name: { type: "string", mapTo: "modelName" }, miles: { type: "int", default: 0 }, }, primaryKey: "_id", }; }
Define Relationship Properties
Tip
See also:
Alternatively, you can define your relationships in your App Services app.
Define a To-One Relationship Property
A to-one relationship maps one property to a single instance of another object type. For example, you can model a manufacturer having at most one car as a to-one relationship.
To define a to-one relationship property, specify the related object type name as the property type.
Important
To-one relationships must be optional
When you declare a to-one relationship in your object model, it must be an optional property. If you try to make a to-one relationship required, Realm throws an exception at runtime.
The following Manufacturer
object schema specifies that a manufacturer may or may not
make a single Car
. If they do make a Car
, Realm links to it through the
car
property:
class Manufacturer extends Realm.Object { static schema = { name: "Manufacturer", properties: { _id: "objectId", // A manufacturer that may have one car car: "Car?", }, }; } class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: "objectId", make: "string", model: "string", miles: "int?", }, }; }
Define a To-Many Relationship Property
A to-many relationship maps one property to zero or more instances of another object type. For example, you can model a manufacturer having any number of cars as a to-many relationship.
To define a to-many relationship property, specify the related object type name as a list.
An application could use the following object schemas to indicate that a Manufacturer
may make multiple Car
objects by including them in its cars
property:
class Manufacturer extends Realm.Object { static schema = { name: "Manufacturer", properties: { _id: "objectId", // A manufacturer that may have many cars cars: "Car[]", }, }; } class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: "objectId", make: "string", model: "string", miles: "int?", }, }; }
Define an Inverse Relationship Property
An inverse relationship property is an automatic backlink relationship. Realm automatically updates implicit relationships whenever an object is added or removed in a corresponding to-many list. You cannot manually set the value of an inverse relationship property.
To define an inverse relationship property, set the property type to
linkingObjects
and specify the object type and property name that define the
relationship to invert.
An application could use the following object schemas to indicate that a Manufacturer
may make many Car
objects and that each Car
should automatically keep track
of which Manufacturer
makes it.
- The
Manufacturer
object'scars
property is defined as a to-many relationship - with
Car
objects and contains all of a given manufacturer's cars.
- The
- The
Car
object'sassignee
property inverts the relationship and - automatically updates to refer back to any
Manufacturer
object that contains the car in itscars
property.
- The
class Manufacturer extends Realm.Object { static schema = { name: "Manufacturer", properties: { _id: "objectId", // A manufacturer that may have many cars cars: "Car[]", }, }; } class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: "objectId", make: "string", model: "string", miles: "int?", // Backlink to the manufacturer. This is automatically updated whenever // this car is added to or removed from a manufacturer's cars list. assignee: { type: "linkingObjects", objectType: "Manufacturer", property: "cars", }, }, }; }
Define an Embedded Object Property
To define a Realm object model with an embedded object (nested Realm
object), set embedded
to true
.
An embedded object exists as nested data inside of a single, specific parent object. It inherits the lifecycle of its parent object and cannot exist as an independent Realm object. Realm automatically deletes embedded objects if their parent object is deleted or when overwritten by a new embedded object instance. Embedded objects cannot have a primary key.
You can reference an embedded object type from parent object types in the same way as a relationship.
The following example requires two parent schemas, Manufacturer
and
Car
. The application requires an embedded child schema Warranty
.
A Manufacturer
object can embed a list of Warranty
objects, whereas a
Car
object can only embed a single Warranty
object.
class Manufacturer extends Realm.Object { static schema = { name: "Manufacturer", properties: { _id: "objectId", name: "string", // Embed an array of objects warranties: { type: "list", objectType: "Warranty" }, }, }; } class Car extends Realm.Object { static schema = { name: "Car", properties: { _id: "objectId", make: "string", model: "string", miles: "int?", // Embed one object warranty: "Warranty", }, }; } class Warranty extends Realm.Object { static schema = { name: "Warranty", embedded: true, properties: { name: "string", termLength: "int", cost: "int", }, }; }
Define an Asymmetric Object
Changed in version 12.2.1.
If you are using Flexible Sync and need to sync a collection unidirectionally
from your device to your Atlas database, you can set the asymmetric
property
on your object schema.
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", }, }; }
class WeatherSensor extends Realm.Object<WeatherSensor> { _id!: Realm.BSON.ObjectId; deviceId!: string; temperatureInFahrenheit!: number; barometricPressureInHg!: number; windSpeedInMph!: number; static schema: ObjectSchema = { 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", }, }; }
In Node.js SDK versions 12.2.0 and earlier, you cannot link from
asymmetric objects to Realm.Object
types. In SDK versions
12.2.1 and later, asymmetric objects can link to Realm.Object
types in addition to embedded objects.
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 Optimize Sync with Data Ingest.
Define Unstructured Data
New in version 12.9.0.
Starting in Node.js SDK version 12.9.0, you can store
collections
of mixed data
within a mixed
property. You can use this feature to model complex data
structures, such as JSON or MongoDB documents, without having to define a
strict data model.
Unstructured data is data that doesn't easily conform to an expected schema, making it difficult or impractical to model to individual data classes. For example, your app might have highly variable data or dynamic data whose structure is unknown at runtime.
Storing collections in a mixed property offers flexibility without sacrificing functionality, including performant synchronization when using Device Sync. And you can work with them the same way you would a non-mixed collection:
You can nest mixed collections up to 100 levels.
You can query on and react to changes on mixed collections.
You can find and update individual mixed collection elements.
However, storing data in mixed collections is less performant than using a structured schema or serializing JSON blobs into a single string property.
To model unstructured data in your app, define the appropriate properties in
your schema as mixed types. You can then set these
mixed
properties as a list or a
dictionary collection of mixed elements.
Note that mixed
cannot represent a set or an embedded object.
Tip
Use a map of mixed data types when the type is unknown but each value will have a unique identifier.
Use a list of mixed data types when the type is unknown but the order of objects is meaningful.