[Realm React] Schema and Embedded Array Confusion

Hi everyone, I’m very confused about how to define schemas when writing in TypeScript (specifically React Native), especially how to write schemas such that embedded objects and arrays work in a Realm database.

How Should I Define Schemas?

The Node.js SDK and React Native SDK say there are two options for defining a realm object model:

JavaScript objects

const Car = {
  name: "Car",
  properties: {
    _id: "objectId",
    make: "string",
    model: "string",
    miles: "int?",
  },
};

JavaScript Classes

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',
  };
}
  • When should I define a Realm object using JavaScript classes/objects?
  • When do I need to define a static schema or type?
  • When should I use a generate function vs passing in an object to the Realm.create function?
  • How does one incorporate all of this with TypeScript?

Mapping Objects/Classes to Schemas

Then there’s a completely separate method of defining schemas, outlined in the Realm React documentation.

If one defines a Realm object like so:

type Label = {
  name: string;
  color: string;
}

export class Task extends Realm.Object<Task> {
  _id: Realm.BSON.UUID;
  userId!: string;
  name!: string;
  description: string;
  createdAt: Date = new Date();
  labels: Label[];
  static primaryKey = '_id';

How does this map to the schema defined in the Realm/Atlas schema UI, which uses JSON?

(Also note that, for me, Label[] does not work.)

Embedded Arrays

When working with embedded objects and arrays—trying to write non-primitives to my database—I run into a situation where the objects and arrays are simply ignored in the realm.write operation.

For example, suppose I have the following object:

const Task: Task = {
  name: 'Example Task',
  description: 'This is an example',
  createdAt: new Date(),
  author: {
    id: 'some_uuid',
    name: 'Author Name',
    authorIconUrl: 'some_url'
  },
  labels: [
     {
        'name': 'Important',
        'color': 'Red'
     },
     {
        'name': 'Family',
        'color': 'Gold'
     },
  ]
}

When I tried writing this in the past, the resultant “Task” in the database would contain everything except for the author and labels properties; it’s as though the realm.write function catches that author and labels are objects and ignores them without any warning/error message/feedback.

I changed my schema implementation (I honestly have no idea what I did, hence the questions about schema declarations above), and magically author and the embedded object showed up. However, the behavior with the array (realm.write quietly ignores it) still happens, and I don’t know how to solve this issue.

Please help! I think I have a barely functional knowledge of realm objects, classes, and schemas, especially in the context of TypeScript, and as such I have no idea how to write embedded objects and arrays of objects to Realm database documents.

  • How does one get embedded objects to work in React Native with TypeScript?
  • How does one write arrays of objects to documents in a Realm database in React Native with TypeScript?

TypeScript

Error:

Classes extending Realm.Object cannot define their own `schema` static, all properties must be defined using TypeScript syntax

Well… :frowning:

Hi @Alexander_Ye,

I’m sorry you’ve encountered so much resistance in getting your app working. We’re aware of these pain points in the docs are working to improve them.

Currently, we’re updating the React Native SDK to default to @realm/react and TypeScript. You can take a look at our progress in this PR. Keep in mind that this work is not complete and may change before it’s merged. We also haven’t gotten to all of the React Native SDK pages yet, so some pages in the staging site are still using the old guidance.

The updated Define a Realm Object Mode and Embedded Objects pages should be more helpful.

Please take a look and let me know if the newer docs help.

1 Like

The article you link “Define a Realm Object Model” recommends a syntax which will throw an error when used:
“Classes extending Realm.Object cannot define their own schema static, all properties must be defined using TypeScript syntax”

Hmm. @Brian_Luther, can you share more info about your app? I can’t reproduce the error. Though I do recall seeing it in the past. What version of realm and realm/react are you using?

Versions I’m using:
realm 11.3.1
@realm/react 0.4.1
@realm/babel-plugin 0.1.1
expo 47.0.12
react-native 0.70.5

Perhaps this has to do with using @realm/babel-plugin to transpile typescript classes into the JSON schema format? I may try removing the babel-plugin and using JSON schemas instead. Many of the docs are written using the JSON schema syntax so it’s hard to tell how to accomplish the same things using the typescript syntax, I think it might be easier to just use the JSON schema.

For example when including a relationship in a class schema defined in the frontend in development mode, I see the JSON schema syntax that the docs describe being generated on the backend in the app services UI. Meaning the type of the field is an ObjectId and there is a relationship definition, eg

{ "createdBy": { "ref": "#/relationship/mongodb-atlas/bolo-6/User", "foreignKey": "_id", "isList": false } }

But when I try to construct an instance in the frontend, it expects the entire related object as an argument rather than the ObjectId of the related object. If I pass just the ObjectId to the constructor, I get the following error:

Error: Exception in HostFunction: Missing value for property 'User.userId'

Where User.userId is another property on the referenced object, seeming to indicate that it wants to be passed the entire referenced object. (Note that this example is slightly confusing, I have an Atlas collection called User to store application data about users, and each document stores the equivalent Realm userId). I am able to construct an object with a relationship to another object only by passing the entire related object to the constructor.

I can’t figure out if the latter issue I described is related to the OP’s or not, but I seem to be hitting a dead-end because many of the docs describe things in terms of JSON syntax and the frontend will only allow me to use the new TypeScript syntax.