@webdocgroup/realm-migrations
Version:
Realm DB migration service for React Native
255 lines (188 loc) • 7.02 kB
Markdown
# Realm DB Migrations For React Native
**Structured, predictable schema migrations for Realm in React Native.**
Realm Migration Service provides a type-safe, step-based, and pluggable migration utility for [Realm](https://www.mongodb.com/docs/atlas/device-sdks/sdk/react-native/) in React Native apps. Define clear migration steps and compose them into a single `migration` function to manage schema evolution with confidence.
## ✨ Features
- ✅ Declarative, versioned migration steps
- ✅ Fully typed in TypeScript
- ✅ Supports forward-only migrations
- ✅ Minimal API surface — just compose and use
- ✅ Designed for React Native + Realm
## ❓ Why
Managing database schema changes in production apps is challenging. Each new version of your schema can introduce breaking changes, and missing a migration step can lead to data corruption or app crashes. Ensuring that every migration runs exactly once, in the correct order, is critical for data integrity and a smooth user experience. This library helps you structure and track migrations, so you can confidently evolve your Realm schemas without fear of missing or duplicating migration logic.
## 📦 Install
Install the package using npm:
```sh
npm install @webdocgroup/realm-migrations
```
## 🚀 Usage
### 1. Define a Realm Schema
Create a schema for your Realm objects. For example, a simple Users schema:
```ts
// ./schemas/Users/V1/index.ts
import type Realm from 'realm';
export const UsersV1Schema: Realm.ObjectSchema = {
name: 'Users',
primaryKey: 'id',
properties: {
id: 'string',
},
};
```
### 2. Create a Migration Step
It's conventional to prefix migration filenames with a timestamp for clarity:
```ts
// ./migrations/202507291336_seed.ts
import Realm from 'realm';
import { Migration } from '@webdocgroup/realm-migrations';
import { UsersV1Schema } from '../schemas/Users/V1';
export const SeedMigration: Migration = {
description: 'Set up the database', // Describe the change for clarity
schemas: [UsersV1Schema], // Only include schemas added/changed since last migration
};
```
### 3. Run Migrations in Your App
Instantiate the migration service and get the latest schema and version:
```ts
// index.ts
import Realm from 'realm';
import { RealmMigrationService } from '@webdocgroup/realm-migrations';
import { SeedMigration } from './migrations/202507291336_seed';
const databaseName = 'default';
// Run migrations and get the up-to-date schema and version
const { schema, schemaVersion } = new RealmMigrationService({
databaseName,
migrations: [SeedMigration],
}).run();
// Instantiate your fully migrated Realm instance with the schema and schema version provided
const realm = new Realm({
path: `${databaseName}.realm`,
schemaVersion,
schema,
});
```
### 4. Add a New Migration (Example)
Suppose you want to add a Comments schema and a new property to Users:
```ts
import Realm from 'realm';
// ./schemas/Comments/V1/index.ts
export const CommentsV1Schema: Realm.ObjectSchema = {
name: 'Comments',
primaryKey: 'id',
properties: {
id: 'string',
comment: 'string',
},
};
// ./schemas/Users/V2/index.ts
export const UsersV2Schema: Realm.ObjectSchema = {
name: 'Users',
primaryKey: 'id',
properties: {
id: 'string',
name: 'string', // New property
},
};
// ./migrations/202508071336_add_comments_and_user_name.ts
import { Migration } from '@webdocgroup/realm-migrations';
import { CommentsV1Schema } from '../schemas/Comments/V1';
import { UsersV2Schema } from '../schemas/Users/V2';
export const AddCommentsAndUserNameMigration: Migration = {
description: 'Add Comments and user name',
schemas: [UsersV2Schema, CommentsV1Schema],
};
```
Update the migration service to include the new migration:
```diff
// index.ts
import Realm from 'realm';
const { schema, schemaVersion } = new RealmMigrationService({
databaseName,
migrations: [
SeedMigration,
+ AddCommentsAndUserNameMigration
],
}).run();
const realm = new Realm({
path: `${databaseName}.realm`,
schemaVersion,
schema,
});
```
### 5. Modify Data During Migration (Example)
```ts
// ./schemas/Users/V3/index.ts
export const UsersV3Schema: Realm.ObjectSchema = {
name: 'Users',
primaryKey: 'id',
properties: {
id: 'string',
firstName: 'string',
lastName: 'string',
},
};
export const SplitUserNameToFirstAndLast: Migration = {
description: 'Split user name to distinct first and last properties',
schemas: [UsersV3Schema],
migrate: (prevRealm, nextRelam) => {
const oldUsers = prevRealm.objects('Users');
const newUsers = nextRelam.objects('Users');
// loop through all objects and set the property in the
// new schema
for (const userIndex in oldUsers) {
const oldUser = oldUsers[userIndex];
const newUser = newUsers[userIndex];
const [firstName, lastName] = oldUser.name.split(' ');
newUser.firstName = firstName;
newUser.lastName = lastName;
}
},
};
```
## 🪝 Hooks
You can add custom functionality to the migration process through hooks. Hooks allow you to control or extend the behavior of the migration service at specific points.
### Supported Hooks
#### **ShouldRunMigrations**
This hook determines whether migrations should run. You can use it to add custom logic to control the execution of migrations.
##### Example: Preventing Migrations
To prevent migrations from running under certain conditions, return early from your custom hook:
```ts
import { ShouldRunMigrationsHook } from '@webdocgroup/realm-migrations';
const customShouldMigrationsRunHook: ShouldRunMigrationsHook = (
props,
next
) => {
// Prevent migrations by returning early
if (props.currentVersion === -1) {
return false;
}
// Or allow the next hook in the chain to execute
return next(props);
};
// Pass your custom hook into the configuration
const migrationService = new RealmMigrationService({
// ...
hooks: {
shouldRunMigrations: [customShouldMigrationsRunHook],
},
});
```
##### Example: Post-Processing After the Hook
To execute custom code after the primary hook has run, you could override or log the result:
```ts
import { ShouldRunMigrationsHook } from '@webdocgroup/realm-migrations';
const customShouldMigrationsRunHook: ShouldRunMigrationsHook = (
props,
next
) => {
// Call the next hooks and capture their result
const result = next(props);
// Log or modify the result as needed
console.log('Will migrations run:', result);
return result;
};
```
By using hooks, you can customize the migration process to suit your application's specific requirements.
## 🧪 Compatibility
This library has been tested with **Realm version 20.x**. It is recommended to use Realm 20 or later for best compatibility.