Inconsistence in data representation using Realm and Typescript

I’m developing an React-Native app right now using Realm.

I’ve created the following test schema in the app:

export const TestSchema = {
  name: 'test',
  properties: {
    _id: 'objectId',
    requiredField: 'string',
    nonRequiredField: 'string?',
  },
  primaryKey: '_id',
};

After the first run and login this the realm-side stuff is created without any problem automatically Using the Realm UI. Going to BUILD → SDKs → Realm Object Models → Languange Typescript I get the following model “suggestion”:

import bson from 'bson';

export type test = {
  _id: bson.ObjectId;
  nonRequiredField?: string;
  owner_id: string;
  requiredField: string;
};

export const testSchema = {
  name: 'test',
  properties: {
    _id: 'objectId',
    nonRequiredField: 'string?',
    owner_id: 'string',
    requiredField: 'string',
  },
  primaryKey: '_id',
};

The schema is the same. So that is good. Now, the Typescript type marks the field “nonRequiredField” as non required (that means that could be undefined, with the ?). One thing to note is that is actually deprecated code, so they should update that.

This seems consistent, but now check the following code:

  realm.write(() => {
    const test: Test = {
      _id: new ObjectId(),
      requiredField: 'this is a test',
    };
    const result = realm
      .create<Test>('test', test, UpdateMode.Modified)
      .toJSON();
    console.log('*** this is the test result', result);
    console.log(
      '*** fieldTypes',
      result.nonRequiredField === null,
      result.nonRequiredField === undefined
    );
  });

Note that i’m doing the toJSON() to dettach the object from realm.

The console output is the following:

[]  LOG      *** this is the test result {"_id": "611b4e687db8f73637a7a5a8", "nonRequiredField": null, "requiredField": "this is a test"}
[]  LOG      *** fieldTypes true false

The field nonRequiredField is being set as null, instead of undefined. And If I check the document in Atlas the value is not null, is undefined:

{"_id":{"$oid":"611b4e687db8f73637a7a5a8"},"requiredField":"this is a test"}

Why I’m getting NULL instead of undefined?

One thing to note is that If I send the value as null in the upsert, and the current value in the document is undefined, the value won’t be set as NULL. That’s good. But this bring the following problem and inconsistency:

Like I said, even if the field is undefined, If I set manually the value as null and then re-send the upsert the field in the document stays as undefined:

result.nonRequiredField = null;
const result2 = realm
  .create<Test>('test', result, UpdateMode.Modified)
  .toJSON();
console.log('*** this is the updated result2', result2);

This is the console output(note the new ids, it’s because is another run):

[]  LOG      *** this is the test result {"_id": "611b4fb57db8f73637a7a5aa", "nonRequiredField": null, "requiredField": "this is a test"}
[]  LOG      *** fieldTypes true false
[]  LOG      *** this is the updated result2 {"_id": "611b4fb57db8f73637a7a5aa", "nonRequiredField": null, "requiredField": "this is a test"}

All OK! Now I will set a value to nonRequiredField:

LOG *** this is the test result {“_id”: “611b50a07db8f73637a7a5af”, “nonRequiredField”: null, “requiredField”: “this is a test”}
LOG *** fieldTypes true false
LOG *** this is the updated result2 {“_id”: “611b50a07db8f73637a7a5af”, “nonRequiredField”: null, “requiredField”: “this is a test”}
LOG *** this is the updated result3 {“_id”: “611b50a07db8f73637a7a5af”, “nonRequiredField”: “Now with value”, “requiredField”: “this is a test”}

This is still fine but now I’m in the situation that I cannot unset that field in a upsert. How can I do it?

If I do the following:

 const test: Test = {
   _id: new ObjectId('611b50a07db8f73637a7a5af'),
   requiredField: 'this is a test updated',
 };
 const result = realm
   .create<Test>('test', test, UpdateMode.Modified)
   .toJSON();
 console.log('*** this is the test update', result);

This wont update the field as undefined. This makes sense so what could be the other option? If I try to send null:

   const test: Test = {
      _id: new ObjectId('611b50a07db8f73637a7a5af'),
      requiredField: 'this is a test updated',
      nonRequiredField: null,
    };
    const result = realm
      .create<Test>('test', test, UpdateMode.Modified)
      .toJSON();
    console.log('*** this is the test update', result);
}

The first problem is that Typescript will mark the nonRequiredField as an error because in the definition is not marked as nullable. This will still run tho, and this is the console result:

[] LOG *** this is the test update {"_id": "611b50a07db8f73637a7a5af", "nonRequiredField": null, "requiredField": "this is a test updated"}

Now the value is set as null. This could seems fine at first sight but If I check in atlas:

{"_id":{"$oid":"611b50a07db8f73637a7a5af"},"owner_id":"611af57f244711fde61ebfbe","requiredField":"this is a test updated","nonRequiredField":null}

nonRequiredField is null! This is actually critical because now it will fail all the schema checks.

Wouldn’t be the correct behavior set the value as undefined if I send null to a non-required-field? Because when the field is actually not set even if I send null the value stays as undefined.