Skip to content

webdocgroup/realm-migrations

Repository files navigation

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 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:

npm install @webdocgroup/realm-migrations

🚀 Usage

1. Define a Realm Schema

Create 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',
    },
};

2. Create a Migration Step

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
};

3. Run Migrations in Your App

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,
});

4. Add a New Migration (Example)

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,
});

5. Modify Data During Migration (Example)

// ./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:

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:

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.