npm package: @realm/babel-plugin
Description: Babel plugin making it easier to declare your Realm schema
Author: MongoDB
Latest version: 0.2.0

# Realm Babel Plugin

## Introduction

The Realm Babel plugin enables defining your Realm models using standard Typescript syntax - no need to define a separate schema.

<table>
<tr>
<th>Before: without the Realm Babel plugin</th>
<th>After: with the Realm Babel plugin</th>
</tr>
<tr>
<td width="50%" valign="top">

```ts
class Task extends Realm.Object<Task, "description"> {
  _id!: Realm.BSON.ObjectId;
  description!: string;
  isComplete!: boolean;

  static schema = {
    name: "Task",
    primaryKey: "_id",
    properties: {
      _id: "objectId",
      description: "string",
      isComplete: {
        type: "bool",
        default: false,
        indexed: true,
      },
    },
  };

  constructor(realm, description: string) {
    super(realm, {
      _id: new Realm.BSON.ObjectId(),
      description,
    });
  }
}
```

</td>
<td width="50%" valign="top">

```ts
class Task extends Realm.Object<Task, "description"> {
  _id = new Realm.BSON.ObjectId();
  description!: string;
  @index
  isComplete = false;

  static primaryKey = "_id";

  constructor(realm, description: string) {
    super(realm, { description });
  }
}
```

</code>
</td>
</tr>
</table>

## Features

- Schema properties can be defined as class properties by using standard TypeScript types or specific `Realm.Types` types, supporting every Realm type
- Support for default values using property initialiser syntax
- Support for specifying additional schema properties (e.g. primary key) using class statics
- Support for indexing and remapping fields using decorators

## Installation

1. Install the `@realm/babel-plugin` npm package:

   `npm install --save-dev @realm/babel-plugin`

2. If you don't already have it installed, install the `@babel/plugin-proposal-decorators` package (only required if you need to use the `@index` or `@mapTo` decorators):

   `npm install --save-dev @babel/plugin-proposal-decorators`

   and enable decorators in your `tsconfig.json` by adding: `"experimentalDecorators": true` to the `compilerOptions`.

3. Update your project's `babel.config.js` to load these two plugins:

   ```js
   // Existing babel.config.js content is commented out
   // module.exports = {
     // presets: ['module:metro-react-native-babel-preset'],

     // --------------------------
     // Add the following plugins:
     plugins: [
       '@realm/babel-plugin',
       ['@babel/plugin-proposal-decorators', { legacy: true }],
     ],
     // --------------------------
   // };

   ```

4. If using React Native, you may need to clear your packager cache for it to pick up the new plugins:

   `npm start -- --reset-cache`

## Usage

### Defining model properties

To define your Realm models when using this plugin, simply create classes which extend `Realm.Object`, and define the model's properties using either supported TypeScript types or `Realm.Types` types (see [supported types](#supported-types)). It is recommended that you use the non-null assertion operator (`!`) after the property name, to tell TypeScript that the property will definitely have a value.

You can use property initialiser syntax to specify a default value for a property, which can either be a static value or a function call for dynamic values.

The recommended pattern for constructing new instances with specified values for properties is to define a constructor which takes the properties as additional arguments. The second type parameter of `Realm.Object` can be used to specify any fields which are required to be specified in the second `fields` when an instance is constructed with `new` - all properties are optional by default.

```ts
import Realm from "realm";

// Specify that the name and description fields are required when creating an instance with `new`
export class Task extends Realm.Object<Task, "name" | "description"> {
  // Property initialiser syntax with a dynamic value - each instance will have a unique ID
  _id = new Realm.BSON.ObjectId();
  name!: string;
  description!: string;
  // Property initializer syntax with a static value
  isComplete = false;
  // Specifying the type of number to be stored in the Realm using Realm.Types
  count!: Realm.Types.Int = 0;
}
```

You can also import `Object` and `Types` directly from `realm`:

```ts
import { Object, Types, BSON } from "realm";

export class Task extends Object<Task, "name" | "description"> {
  _id = BSON.ObjectId();
  name: string;
  description!: string;
  isComplete = false;
  count!: Types.Int;
}
```

#### Supported types

This plugin supports standard TypeScript types wherever possible, to make defining your model as natural as possible. Some Realm types do not have a direct TypeScript equivalent (e.g. `double`, `int` and `float` are all represented by `number` in TypeScript), so in these cases you should use the types provided by `Realm.Types`.

As a rule, we recommend using TypeScript types where possible, and using `Realm.Types` where you cannot, but you can also exclusively use types from `Realm.Types` if preferred.

Types which are provided by Realm (e.g. `Realm.List`) are exported from both the top-level `Realm` namespace, and from `Realm.Types` - you can use either variant in your models.

The supported types are shown in the table below. See [the Realm documentation](https://www.mongodb.com/docs/realm/sdk/react-native/data-types/field-types/) and [SDK documentation](https://www.mongodb.com/docs/realm-sdks/js/latest/Realm.html#~PropertyType) for more details on each type.

| Realm.Types type                             | Realm schema type | TypeScript type | Realm type              | Notes                                                                                  |
| -------------------------------------------- | ----------------- | --------------- | ----------------------- | -------------------------------------------------------------------------------------- |
| `Types.Bool`                                 | `bool`            | `boolean`       |                         |                                                                                        |
| `Types.String`                               | `string`          | `string`        |                         |                                                                                        |
| `Types.Int`                                  | `int`             |                 |                         |                                                                                        |
| `Types.Float`                                | `float`           |                 |                         |                                                                                        |
| `Types.Double`                               | `double`          | `number`        |                         | Double is the default number type                                                      |
| `Types.Decimal128`                           | `decimal128`      |                 | `Realm.BSON.Decimal128` |                                                                                        |
| `Types.ObjectId`                             | `objectId`        |                 | `Realm.BSON.ObjectId`   |                                                                                        |
| `Types.UUID`                                 | `uuid`            |                 | `Realm.BSON.UUID`       |                                                                                        |
| `Types.Date`                                 | `date`            | `Date`          |                         |                                                                                        |
| `Types.Data`                                 | `data`            | `ArrayBuffer`   |                         |                                                                                        |
| `Types.List<T>`                              | `type[]`          |                 | `Realm.List<T>`         | `T` is the type of objects in the list                                                 |
| `Types.Set<T>`                               | `type<>`          |                 | `Realm.Set<T>`          | `T` is the type of objects in the set                                                  |
| `Types.Dictionary<T>`                        | `type{}`          |                 | `Realm.Dictionary<T>`   | `T` is the type of objects in the dictionary                                           |
| `Types.Mixed`                                | `mixed`           |                 | `Realm.Mixed`           |                                                                                        |
| <code>Types.LinkingObjects<T,&nbsp;N></code> | `linkingObjects`  |                 |                         | `T` is the type of objects, `N` is the property name of the relationship (as a string) |

### Specifying schema properties as `static`s

Additional schema properties can be specified by adding `static` properties to your class, as shown in the table below. See [the Realm documentation](https://www.mongodb.com/docs/realm/sdk/node/examples/define-a-realm-object-model/) for more details.

| Static property | Type      | Notes                                                                      |
| --------------- | --------- | -------------------------------------------------------------------------- |
| `name`          | `string`  | Specifies the name of the Realm schema. Defaults to your class name.       |
| `primaryKey`    | `string`  | Specifies the name of a property to be used as the primary key.            |
| `embedded`      | `boolean` | Specifies this is an embedded schema.                                      |
| `asymmetric`    | `boolean` | Specifies this schema should sync unidirectionally if using flexible sync. |

For example:

```ts
import Realm from "realm";

export class Task extends Realm.Object<Task, "description"> {
  _id = new Realm.BSON.ObjectId();
  description!: string;
  isComplete = false;

  static primaryKey = "_id";
}
```

### Using decorators to index and remap properties

The `@realm/babel-plugin` package exports decorators to allow you to specify certain properties should be indexed (using the `@index` decorators) or should remap to a Realm schema property with a different name (using the `@mapTo` decorator). To learn more about this functionality, see [the documentation](https://www.mongodb.com/docs/realm/sdk/react-native/examples/define-a-realm-object-model/#index-a-property).

Note that use of decorators requires using the `@babel/plugin-proposal-decorators` plugin and for `experimentalDecorators` to be enabled in your `tsconfig.json`. There is currently no way to specifying properties to be indexed or remapped without using decorators.

This table shows the available decorators:

| Decorator | Parameters                    | Notes                                                                                                                                                                           |
|-----------|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `index`   | none or `("full-text")`       | Specifies that the decorated property should be indexed by Realm. Providing the string "full-text" specifies that the property should be indexed for full-text search by Realm. |
| `mapTo`   | `(realmPropertyName: string)` | Specifies that the decorated property should be stored as `realmPropertyName` in the Realm database.                                                                            |

The example below shows both decorators in use:

```ts
import Realm from "realm";
import { mapTo, index } from "@realm/babel-plugin";

export class Task extends Realm.Object {
  _id!: Realm.BSON.ObjectId;
  // Add an index to the `assignee` property
  @index
  assignee!: string;
  // Specify that the `description` property should be indexed for full-text search
  @index("full-text")
  description!: string;
  // Specify that the `isComplete` property should be stored as `complete` in the Realm database
  @mapTo("complete")
  isComplete = false;
}
```

## Restrictions

### All class properties will be added to the Realm schema

There is currently no way to specify a property on your class which should not be persisted to the Realm.

### Classes extending Realm.Object cannot be constructed with `new` outside of a write transaction

This plugin does not change the behaviour of `Realm.Object`, which cannot be constructed using `new` outside of a write transaction - there is no concept of a Realm.Object which is not stored in a Realm. Constructing a Realm object with `new` inside a write transaction will create a new object in the Realm - see [class-based models in the CHANGELOG](/CHANGELOG.md#enhancements-4).

## Debugging your model

To generate the output for your model (let's say it's located in `./models/task.ts`) you can run the babel transpiler manually on the file:

```
npm install --save-dev @babel/cli @babel/preset-typescript
npx babel --presets @babel/preset-typescript --plugins @realm/babel-plugin ./models/task.ts
```