Structured, predictable schema migrations for Realm in React Native.
Realm Migration Service provides a type-safe, step-based, and pluggable migration utility for Realm in React Native apps. Define clear migration steps and compose them into a single migration function to manage schema evolution with confidence.
- ✅ Declarative, versioned migration steps
- ✅ Fully typed in TypeScript
- ✅ Supports forward-only migrations
- ✅ Minimal API surface — just compose and use
- ✅ Designed for React Native + Realm
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 the package using npm:
npm install @webdocgroup/realm-migrationsCreate a schema for your Realm objects. For example, a simple Users schema:
// ./schemas/Users/V1/index.ts
import type Realm from 'realm';
export const UsersV1Schema: Realm.ObjectSchema = {
name: 'Users',
primaryKey: 'id',
properties: {
id: 'string',
},
};It's conventional to prefix migration filenames with a timestamp for clarity:
// ./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
};Instantiate the migration service and get the latest schema and version:
// 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,
});Suppose you want to add a Comments schema and a new property to Users:
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:
// 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,
});// ./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;
}
},
};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.
This hook determines whether migrations should run. You can use it to add custom logic to control the execution of migrations.
To prevent migrations from running under certain conditions, return early from your custom hook:
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],
},
});To execute custom code after the primary hook has run, you could override or log the result:
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.
This library has been tested with Realm version 20.x. It is recommended to use Realm 20 or later for best compatibility.