Skip to main content

RxDB as a NeDB Alternative for Node.js, Electron, and the Browser

If you arrived here, you are likely running a project that depends on NeDB and you are looking for a maintained replacement. NeDB served many Node.js, Electron, and browser applications well during its active years, but the project has been frozen since 2016. Modern apps need schema validation, observable queries, multi-tab coordination, and replication, and a database that still receives security updates. RxDB fills that gap while keeping the embedded, document-oriented model that NeDB users are familiar with.

RxDB JavaScript Database

A Short History of NeDB

NeDB (Node Embedded Database) was created around 2013 by Louis Chatriot. It became widely adopted because it offered a familiar MongoDB-like API without requiring a server process. A NeDB database was just a file, and the library appended each operation as a new line, then compacted the file in the background. That design made NeDB attractive for:

  • Node.js scripts and small servers that wanted a local store without setting up MongoDB.
  • Electron and nw.js desktop apps that needed to persist user data between sessions.
  • Browser-based applications through storage adapters that wrote to IndexedDB or localStorage.

The query language mirrored MongoDB, so developers could use operators like $gt, $in, and $regex against documents, build indexes on fields, and project results. The last commit to the original repository landed in 2016, and the project was eventually marked as unmaintained. Several community forks exist, but none have grown into a widely supported successor.

What is RxDB?

RxDB is a reactive, NoSQL, offline-first database for JavaScript. It runs in the browser, in Node.js, in Electron, in React Native, and in any other JavaScript runtime. Documents are stored locally through a swappable storage layer, queries return observables that emit on every change, and an open replication protocol keeps clients in sync with any backend.

RxDB has been under continuous development for nearly a decade and ships regular releases, security fixes, and new features. It treats the local database as the primary source of truth, which matches how teams build local-first applications today.

Where NeDB Falls Short

NeDB still works for trivial use cases, but production apps tend to hit hard limits:

1. Unmaintained for Nearly a Decade

The repository has had no updates since 2016. Reported issues sit open, dependency vulnerabilities are not patched, and the codebase predates many features of modern Node.js such as worker threads and async iterators.

2. Single-File Persistence Risks Corruption

NeDB writes operations as appended lines and rewrites the entire file during compaction. A crash during compaction can leave the database in a damaged state, and there is no built-in recovery beyond manual file inspection. Larger datasets also slow startup, because NeDB reloads the whole file into memory.

3. No Replication

NeDB has no sync layer. Sharing data between two devices, between a desktop client and a server, or between two browser tabs requires a custom solution that the developer has to build, test, and maintain.

4. No Observable Queries

Queries return promises or callbacks. To keep a UI in sync with the data, the application has to re-run queries manually after every write. That coupling between writes and reads quickly becomes the source of bugs in any non-trivial UI.

5. No Multi-Tab Coordination

A NeDB database opened in two browser tabs has no concept of shared state. Writes from one tab are invisible to the other unless the tabs communicate themselves through a BroadcastChannel or similar primitive.

6. No Schema Validation

NeDB is schemaless. Every document can have any shape, which sounds flexible at first but quickly leads to runtime errors when fields drift over time. There is no migration system either, so changing data shape has to be handled by the application.

How RxDB Solves These Problems

RxDB keeps the document-oriented model that NeDB users like, and adds the features missing from NeDB:

  • Active maintenance: continuous releases with security and feature updates.
  • Schema validation: every collection is defined by an RxSchema based on JSON Schema, with versioning and migrations.
  • MongoDB-style queries: the RxQuery API supports the same operators NeDB users are accustomed to, including $gt, $in, $regex, $elemMatch, sorting, and skip/limit.
  • Observable queries: queries expose RxJS observables, and the UI updates automatically when results change. See Reactivity.
  • Multi-tab support: writes in one tab are streamed to all other tabs through BroadcastChannel, with conflict-safe storage handling under the hood.
  • Replication: the Sync Engine connects to any HTTP, GraphQL, CouchDB, WebRTC, or custom backend.
  • Durable storages: RxDB ships with battle-tested storage adapters. Use SQLite or the filesystem on Node.js and Electron, use IndexedDB, OPFS, Dexie, or SQLite-WASM in the browser.

Code Sample: From NeDB to RxDB

A typical NeDB workflow looks like this:

const Datastore = require('nedb');
const db = new Datastore({ filename: 'tasks.db', autoload: true });
 
db.insert({ _id: 't1', title: 'Write report', done: false }, (err, doc) => {
    // ...
});
 
db.find({ done: false }).sort({ title: 1 }).exec((err, docs) => {
    // ...
});

The same workflow in RxDB looks like this:

import { createRxDatabase } from 'rxdb/plugins/core';
import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
 
const db = await createRxDatabase({
    name: 'tasks',
    storage: getRxStorageLocalstorage()
});
 
await db.addCollections({
    tasks: {
        schema: {
            title: 'task schema',
            version: 0,
            primaryKey: 'id',
            type: 'object',
            properties: {
                id: { type: 'string', maxLength: 100 },
                title: { type: 'string' },
                done: { type: 'boolean' }
            },
            required: ['id', 'title', 'done']
        }
    }
});
 
await db.tasks.insert({ id: 't1', title: 'Write report', done: false });
 
const openTasks = await db.tasks
    .find({ selector: { done: false }, sort: [{ title: 'asc' }] })
    .exec();

The query syntax stays close to MongoDB, so most NeDB selectors translate directly. See the full RxQuery documentation for supported operators.

Code Sample: Subscribing to a Query

NeDB has no equivalent for the snippet below. With RxDB, the result list updates automatically whenever a matching document changes:

db.tasks
    .find({ selector: { done: false } })
    .$.subscribe(tasks => {
        renderTaskList(tasks);
    });
 
// Inserting a new task elsewhere in the app
await db.tasks.insert({ id: 't2', title: 'Send invoice', done: false });
// The subscriber above receives the updated array immediately.

This pattern removes the boilerplate of re-running queries after each write and keeps your UI consistent with the database state.

Migration Notes

Most NeDB projects can move to RxDB in a few steps:

  1. Define a schema for every NeDB datastore. Inspect a sample of existing documents to derive the field types and required properties. The schema is required by RxCollection and unlocks validation and migrations.
  2. Pick a storage. On Node.js or Electron, use a durable storage like SQLite (see Node.js Database and Electron Database). In the browser, IndexedDB or OPFS are good defaults.
  3. Import data. Read the existing NeDB file with the legacy library, normalize each document so it matches the new schema, and call bulkInsert on the corresponding RxDB collection. NeDB uses _id as the primary key, while RxDB lets you choose any field, so a small rename is often required.
  4. Translate queries. Most selectors port over with no changes. Replace callback APIs with async/await, and replace manual re-runs with .$ observables where you want reactive updates.
  5. Add replication if needed. If your old setup synced data through a custom mechanism, replace it with the official RxDB replication.

A migration script that runs once on first launch is often enough. After a successful import, the legacy NeDB file can be deleted.

FAQ

Is NeDB maintained?

No. The original NeDB repository has not received commits since 2016 and is archived. Issues remain open, and dependency security advisories are not addressed. Community forks exist, but none provide the long-term support that an active project like RxDB offers.

Can I keep MongoDB-style queries in RxDB?

Yes. RxDB queries use the same selector format as MongoDB and NeDB, including operators like $gt, $lt, $in, $nin, $regex, and $elemMatch, plus sort, skip, and limit. See the RxQuery documentation for the full list.

Is RxDB safe to use in Electron?

Yes. RxDB ships official guidance and storage options for Electron, including SQLite-backed storages that store data on the local filesystem. The Electron Database page covers configuration in both the main and renderer processes, including multi-window setups.

How do I migrate data from NeDB to RxDB?

Read the existing NeDB file with the legacy library, define an RxDB schema that matches the documents, and call bulkInsert on the new collection. Rename _id to your chosen primary key while you copy the data. After verifying the import, the old NeDB file can be removed.

Comparison Table

FeatureNeDBRxDB
Maintenance statusLast commit 2016, archivedActive, regular releases
Query languageMongoDB-likeMongoDB-like (RxQuery)
Schema validationNoneJSON Schema based (RxSchema)
Observable queriesNoYes, via RxJS (Reactivity)
Multi-tab supportNoYes
ReplicationNoneBuilt-in (Sync Engine)
Browser storageIndexedDB adapterIndexedDB, OPFS, Dexie, SQLite-WASM
Node.js storageSingle-file append logSQLite, filesystem, memory (Node.js Database)
Electron storageSingle-file append logDurable storages (Electron Database)
MigrationsManualBuilt-in schema migrations
TypeScript supportCommunity typingsFirst-class TypeScript
EncryptionNoneOptional plugin
CompressionNoneOptional plugin

Follow Up

RxDB gives NeDB users a maintained, document-oriented database with the same MongoDB-style query language, plus the features modern apps require: schemas, observable queries, multi-tab coordination, and replication. Read the Quickstart, pick a storage that fits your runtime, and port your collections over with a short migration script.

More resources: