Why Migrate to Cosmictron?

MongoDB Realm's deprecation left thousands of applications without a clear migration path. While Firebase and Supabase are common alternatives, they don't offer the same architecture as Realm. Cosmictron does—and improves upon it.

Migration Timeline

Most teams complete their migration in 1-2 days for simple apps, or 1-2 weeks for complex production systems with extensive Realm Functions and Triggers.

Feature MongoDB Realm Cosmictron
Offline Sync Device Sync (now deprecated) Planned (CRDT-based)
Serverless Functions Realm Functions Reducers (WASM/V8)
Real-time Sync Live Queries DBSP Incremental Subscriptions
Data Storage MongoDB Atlas Embedded (in-memory with WAL)
Triggers Database Triggers Lifecycle Hooks
Hosting Managed (Atlas) Self-hosted or Managed

Concept Mapping

Device Sync → Real-time Subscriptions

Realm's Device Sync kept local MongoDB collections in sync with the cloud. In Cosmictron, clients subscribe to SQL queries and receive incremental deltas automatically.

TypeScript
// Realm (old)
const collection = realm.objects('Task');
const listener = collection.addListener(() => {
  console.log('Tasks changed');
});

// Cosmictron (new)
const subscription = await client.subscribe(
  'SELECT * FROM tasks WHERE user_id = ?',
  { user_id: currentUser.id }
);
subscription.on('update', ({ inserts, deletes, updates }) => {
  inserts.forEach(task => console.log('New task:', task));
});

Realm Functions → Reducers

Realm Functions were serverless JavaScript functions. Cosmictron uses reducers—ACID-transactional functions written in Rust or TypeScript that run inside the database.

Rust
// Realm Function (old)
exports = async function createTask(title) {
  const collection = context.services.get("mongodb-atlas")
    .db("app").collection("tasks");
  return await collection.insertOne({ title, createdAt: new Date() });
};

// Cosmictron Reducer (new)
#[reducer]
pub fn create_task(title: String) {
  let task = Task {
    id: 0, // auto-assigned
    title,
    created_at: ctx.timestamp(),
    user_id: ctx.sender(),
  };
  ctx.db.tasks().insert(&task);
}

Triggers → Lifecycle Hooks

Realm Triggers responded to database changes. Cosmictron provides lifecycle hooks that run during module initialization, client connection, and disconnection.

Rust
// Realm Trigger (old)
exports = async function onUserChange(changeEvent) {
  const user = changeEvent.fullDocument;
  await context.services.get("mongodb-atlas")
    .db("app").collection("logs")
    .insertOne({ event: 'user_updated', user });
};

// Cosmictron Lifecycle (new)
#[reducer(lifecycle = "client_connected")]
pub fn on_client_connected() {
  ctx.db.user_presence().insert(&Presence {
    user_id: ctx.sender(),
    status: "online",
    last_seen: ctx.timestamp(),
  });
}

Migration Steps

1. Export Your Realm Data

Before migrating, export your data from MongoDB Atlas:

  1. Go to your Atlas cluster
  2. Use mongodump to export collections
  3. Convert BSON to JSON for easier processing

2. Define Your Schema

Convert your Realm object models to Cosmictron table definitions:

Rust
// Realm Schema (old)
const TaskSchema = {
  name: 'Task',
  properties: {
    _id: 'objectId',
    title: 'string',
    completed: 'bool',
    user_id: 'string',
    createdAt: 'date',
  }
};

// Cosmictron Table (new)
#[table(name = "tasks", public)]
pub struct Task {
  #[primary_key]
  #[auto_inc]
  pub id: u64,
  pub title: String,
  pub completed: bool,
  #[index]
  pub user_id: String,
  #[index]
  pub created_at: u64,
}

3. Migrate Business Logic

Convert Realm Functions to reducers. Each Realm Function typically becomes one reducer:

  • CRUD operations → Reducers with #[reducer]
  • Authentication logic → RLS policies + identity checks
  • Validation → Type system + reducer logic

4. Update Client Code

Replace Realm SDK calls with Cosmictron client SDK:

TypeScript
// Realm (old)
const realm = await Realm.open({ schema: [TaskSchema] });
await realm.write(() => {
  realm.create('Task', { title: 'Buy milk', completed: false });
});

// Cosmictron (new)
const client = new CosmictronClient('ws://localhost:3000');
await client.connect();
await client.callReducer('tasks', 'create_task', {
  title: 'Buy milk'
});

5. Deploy and Test

  1. Start Cosmictron server: cosmictron-server
  2. Deploy your module: cosmictron deploy ./module.wasm
  3. Run integration tests
  4. Migrate production data
  5. Update DNS and go live

Common Migration Challenges

Offline-First Apps

Realm's offline sync was a key feature. Cosmictron doesn't have full offline support yet (planned for v2), but you can implement optimistic updates:

  • Queue mutations locally when disconnected
  • Apply optimistic updates to UI immediately
  • Sync when connection restored

Complex Aggregations

Realm allowed complex aggregation pipelines. In Cosmictron, use SQL subscriptions with DBSP incremental maintenance:

SQL
-- Real-time aggregation with automatic updates
SELECT 
  status,
  COUNT(*) as count,
  MAX(created_at) as last_updated
FROM tasks
WHERE user_id = ?
GROUP BY status

User Authentication

Realm provided built-in auth. In Cosmictron, bring your own auth (JWT recommended) and use RLS for authorization:

Rust
#[table(name = "tasks", public)]
#[rls(read = "user_id = ctx.sender()")]
#[rls(write = "user_id = ctx.sender()")]
pub struct Task {
  #[primary_key] pub id: u64,
  #[index] pub user_id: Identity,
  pub title: String,
}

Need Help?

Migration can be complex. We're here to help:

Ready to migrate?

Start with our quick start guide or dive into the documentation.

Read the Docs → Back to Home