docs-src/docs/articles/alternatives/mongodb-realm-alternative.md
Teams that built mobile and web applications on top of MongoDB Realm and the Atlas Device SDKs are now in a difficult position. In September 2024, MongoDB announced the deprecation of the Atlas Device SDKs and Atlas Device Sync, with end of life targeted for September 2025. Applications that still rely on Realm for client storage and bidirectional sync need a JavaScript friendly replacement that does not lock the project to a single cloud vendor and that will keep receiving updates well past 2025.
This page explains why RxDB is a strong replacement for Realm in JavaScript, TypeScript, React Native, Electron, and browser environments. It covers the history of Realm, the technical shortcomings that existed even before the deprecation announcement, the features RxDB provides today, code samples for schema definition and replication to a MongoDB-backed HTTP endpoint, and practical migration notes.
<center> <a href="https://rxdb.info/"></a>
Realm started in 2014 as a mobile database for Android and iOS, evolving from an earlier project called TightDB. It was positioned as a replacement for SQLite but the storage model resembled an object store more than a relational database. Realm Mobile Platform later added bidirectional sync between devices and a self-hostable server. Bindings for additional languages followed, including JavaScript through realm-js for Node.js and React Native.
In 2019 MongoDB acquired Realm and folded it into the MongoDB Atlas product line. The local database engine and the sync layer were rebranded as the Atlas Device SDKs and Atlas Device Sync, and the focus shifted toward replication against MongoDB Atlas in the cloud. Self-hosting the sync server stopped being a supported path. In September 2024, MongoDB published the deprecation notice for the Atlas Device SDKs and Atlas Device Sync. New project sign ups were closed, and existing customers were given until September 2025 before end of life.
For JavaScript teams this timeline is short. Migrating client storage, replication, and conflict handling without losing data takes planning, which is why choosing a long term alternative now matters.
RxDB is a local-first JavaScript database that stores data on the client and replicates it to any backend. It runs in the browser, Node.js, Electron, React Native, Capacitor, Deno, and Bun. Data is organized into collections with a JSON schema, queried with a MongoDB style query language, and observed through RxJS observables. Replication is pluggable: there are adapters for HTTP, GraphQL, WebRTC, CouchDB, Firestore, Supabase, NATS, and more. The storage layer is pluggable as well, so the same codebase can use IndexedDB, OPFS, SQLite, Memory, or a custom engine.
Unlike Realm, RxDB is written in TypeScript and ships as plain JavaScript. There are no per platform native bindings to maintain, and the same query and replication code works on every supported runtime.
The deprecation is the most pressing reason to migrate, but Realm had structural limitations long before the 2024 announcement.
realm-js is a string based filter syntax with a smaller set of operators than the MongoDB query language. Aggregations, joins across collections, and complex nested filters often required client side post processing.RxDB addresses each of the points above.
op-sqlite on React Native, and the rest of the database is plain JS that runs anywhere.$gt, $in, $regex, $elemMatch operators documented under RxQuery. Developers coming from MongoDB or Mongoose feel at home.The following snippet defines a todos collection and subscribes to a reactive query. Compare this to the Realm equivalent that requires a Realm class definition and a synchronous realm.objects(...) call wrapped in a change listener.
import { createRxDatabase } from 'rxdb/plugins/core';
import { getRxStorageDexie } from 'rxdb/plugins/storage-dexie';
const db = await createRxDatabase({
name: 'tasksdb',
storage: getRxStorageDexie()
});
await db.addCollections({
todos: {
schema: {
version: 0,
primaryKey: 'id',
type: 'object',
properties: {
id: { type: 'string', maxLength: 64 },
title: { type: 'string' },
done: { type: 'boolean' },
createdAt: { type: 'number' }
},
required: ['id', 'title', 'done', 'createdAt']
}
}
});
// Reactive query, emits whenever matching documents change.
const openTodos$ = db.todos.find({
selector: { done: false },
sort: [{ createdAt: 'desc' }]
}).$;
openTodos$.subscribe(todos => {
console.log('open todos:', todos.map(t => t.title));
});
await db.todos.insert({
id: 't1',
title: 'Replace Realm with RxDB',
done: false,
createdAt: Date.now()
});
RxDB does not talk to MongoDB directly from the client, which is the right architectural choice because the database driver belongs on the server. Instead the HTTP replication plugin calls REST endpoints that read from and write to MongoDB on the server. The endpoints follow the pull and push pattern documented in the replication guide.
import { replicateRxCollection } from 'rxdb/plugins/replication';
const replicationState = replicateRxCollection({
collection: db.todos,
replicationIdentifier: 'todos-mongo-http',
live: true,
pull: {
async handler(checkpointOrNull, batchSize) {
const updatedAt = checkpointOrNull?.updatedAt ?? 0;
const id = checkpointOrNull?.id ?? '';
const response = await fetch(
`/api/todos/pull?updatedAt=${updatedAt}&id=${id}&limit=${batchSize}`
);
const data = await response.json();
return {
documents: data.documents,
checkpoint: data.checkpoint
};
}
},
push: {
async handler(changeRows) {
const response = await fetch('/api/todos/push', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(changeRows)
});
const conflicts = await response.json();
return conflicts;
}
}
});
replicationState.error$.subscribe(err => console.error('replication error', err));
On the server side, /api/todos/pull runs a MongoDB find({ updatedAt: { $gte: ... } }) sorted by updatedAt and id, and /api/todos/push performs a conditional update keyed on the document revision. This pattern preserves the Atlas Device Sync developer experience without depending on Atlas.
A migration from Realm typically follows these steps.
collection.bulkInsert(docs) on first launch after the migration release. Mark the migration as complete in localStorage so it only runs once.realm.objects('Todo').filtered('done == false SORT(createdAt DESC)') becomes db.todos.find({ selector: { done: false }, sort: [{ createdAt: 'desc' }] }).collection.addListener becomes query.$.subscribe. Most UI frameworks already integrate with RxJS or with hooks like useRxQuery.A staged rollout where both databases run side by side for one release is the safest path. RxDB writes the authoritative copy, Realm stays read only, and the next release removes Realm.
Yes. In September 2024 MongoDB announced the deprecation of the Atlas Device SDKs and Atlas Device Sync. End of life is targeted for September 2025, and new project sign ups have already been closed. Existing apps will continue to function until EOL, after which the service will be shut down.
</details> <details> <summary>Can RxDB still talk to MongoDB?</summary>Yes, through a server side adapter. The RxDB client uses the HTTP replication plugin to call REST endpoints, and those endpoints read from and write to MongoDB on the server. Direct client to MongoDB connections are not supported, which is the correct security boundary for any production app.
</details> <details> <summary>How do I migrate Realm objects to RxDB?</summary>Define an RxSchema for each Realm class, export every Realm object to JSON on app launch, and call collection.bulkInsert(docs) to load them into RxDB. Track migration completion in persistent storage so the import runs exactly once per device.
Yes. RxDB has first class support for React Native using the SQLite or memory storage adapters. The same schema and query code runs in the browser, in Node.js, in Electron, and on React Native without modification.
</details> <details> <summary>Is RxDB free for commercial use?</summary>The RxDB core is open source under the Apache 2.0 license and free for commercial use. There is also a Premium offering with extra storage adapters, encryption modes, and performance plugins. The free core is sufficient for most applications.
</details>| Feature | MongoDB Realm / Atlas Device SDK | RxDB |
|---|---|---|
| Status | Deprecated, EOL September 2025 | Active, regular releases |
| Implementation | C++ core with native bindings per platform | Pure TypeScript, no native bindings |
| Backend | MongoDB Atlas only | Any HTTP, GraphQL, WebRTC, CouchDB, Firestore, Supabase, custom |
| Query language | Realm filter strings | MongoDB style selectors with $gt, $in, $regex, $elemMatch |
| Reactive queries | Change listeners | RxJS observables |
| Multi tab | Limited in browser | Built in leader election |
| Encryption | File level | Per field with AES |
| Schema migrations | Imperative migration callbacks | Declarative migration strategies per schema version |
| Offline first | Yes | Yes |
| React Native | Yes through native binding | Yes through SQLite or memory adapter |
| Browser | Limited via WebAssembly | First class through IndexedDB, OPFS, Memory |
| License | Apache 2.0 SDK, proprietary sync | Apache 2.0 core, optional Premium add-ons |
| Self hosting | Not supported | Fully supported |
For teams currently running on Realm, the EOL date in September 2025 is firm. Starting the migration to RxDB now leaves time for a staged rollout, a tested HTTP replication layer against MongoDB, and a clean removal of the Atlas Device SDK before support ends.