RxDB
A fast, offline-first, reactive database for JavaScript Applications
README
A fast, offline-first, reactive Database for JavaScript Applications
What is RxDB?
RxDB (short for Reactive Database) is an offline-first, NoSQL-database for JavaScript Applications like Websites, hybrid Apps, Electron-Apps, Progressive Web Apps and Node.js. Reactive means that you can not only query the current state, but subscribe to all state changes like the result of a query or even a single field of a document. This is great for UI-based realtime applications in a way that makes it easy to develop and also has great performance benefits but can also be used to create fast backends in Node.js.
RxDB provides an easy to implement protocol for realtime replication with your existing infrastructure or any compliant CouchDB endpoint.
RxDB is based on a storage interface that enables you to swap out the underlying storage engine. This increases code reuse because you can use the same database code for different JavaScript environments by just switching out the storage settings.
Multiplayer realtime applications
Replicate with your existing infrastructure
There are also plugins to replicate with any CouchDB endpoint or over GraphQL and REST or even P2P.
Flexible storage layer
Browsers Node.js Electron React Native Cordova/Phonegap Capacitor NativeScript Flutter
Quick start
Install
- ```sh
- npm install rxdb rxjs --save
- ```
Store data
- ``` js
- import {
- createRxDatabase
- } from 'rxdb';
- /**
- * For browsers, we use the dexie.js based storage
- * which stores data in IndexedDB.
- * In other JavaScript runtimes, we can use different storages.
- * @link https://rxdb.info/rx-storage.html
- */
- import { getRxStorageDexie } from 'rxdb/plugins/dexie';
- // create a database
- const db = await createRxDatabase({
- name: 'heroesdb', // the name of the database
- storage: getRxStorageDexie()
- });
- // add collections
- await db.addCollections({
- heroes: {
- schema: mySchema
- }
- });
- // insert a document
- await db.heroes.insert({
- name: 'Bob',
- healthpoints: 100
- });
- ```
Query data once
- ``` js
- const aliveHeroes = await db.heroes.find({
- selector: {
- healthpoints: {
- $gt: 0
- }
- }
- }).exec(); // the exec() returns the result once
- ```
Observe a Query
- ``` js
- await db.heroes.find({
- selector: {
- healthpoints: {
- $gt: 0
- }
- }
- })
- .$ // the $ returns an observable that emits each time the result set of the query changes
- .subscribe(aliveHeroes => console.dir(aliveHeroes));
- ```
More Features (click to toggle)
Subscribe to events, query results, documents and event single fields of a document
RxDB implements rxjs to make your data reactive.This makes it easy to always show the real-time database-state in the dom without manually re-submitting your queries.
- ``` js
- db.heroes
- .find()
- .sort('name')
- .$ // <- returns observable of query
- .subscribe( docs => {
- myDomElement.innerHTML = docs
- .map(doc => '<li>' + doc.name + '</li>')
- .join();
- });
- ```
MultiWindow/Tab
RxDB supports multi tab/window usage out of the box. When data is changed at one browser tab/window or Node.js process, the change will automatically be broadcasted to all other tabs so that they can update the UI properly.
EventReduce One big benefit of having a realtime database is that big performance optimizations can be done when the database knows a query is observed and the updated results are needed continuously. RxDB internally uses the Event-Reduce algorithm. This makes sure that when you update/insert/remove documents,
the query does not have to re-run over the whole database but the new results will be calculated from the events. This creates a huge performance-gain with zero cost.
Use-Case-Example
- ``` js
- const query = usersCollection.find().where('loggedIn').eq(true).sort('points');
- query.$.subscribe(users => {
- document.querySelector('body').innerHTML = users
- .reduce((prev, cur) => prev + cur.username+ '<br/>', '');
- });
- ```
- ``` js
- await anyUser.atomicPatch({loggedIn: false});
- ```
Schema
Schemas are defined via [jsonschema](http://json-schema.org/) and are used to describe your data.
- ``` js
- const mySchema = {
- title: "hero schema",
- version: 0, // <- incremental version-number
- description: "describes a simple hero",
- primaryKey: 'name', // <- 'name' is the primary key for the coollection, it must be unique, required and of the type string
- type: "object",
- properties: {
- name: {
- type: "string",
- maxLength: 30
- },
- secret: {
- type: "string",
- },
- skills: {
- type: "array",
- maxItems: 5,
- uniqueItems: true,
- item: {
- type: "object",
- properties: {
- name: {
- type: "string"
- },
- damage: {
- type: "number"
- }
- }
- }
- }
- },
- required: ["color"],
- encrypted: ["secret"] // <- this means that the value of this field is stored encrypted
- };
- ```
Mango / Chained queries RxDB can be queried by standard NoSQL mango queries like you maybe know from other NoSQL Databases like mongoDB.
Also you can use the query-builder plugin to create chained mango-queries.
- ``` js
- // normal query
- myCollection.find({
- selector: {
- name: {
- $ne: 'Alice'
- },
- age: {
- $gt: 67
- }
- },
- sort: [{ age: 'desc' }],
- limit: 10
- })
- // chained query
- myCollection
- .find()
- .where('name').ne('Alice')
- .where('age').gt(18).lt(67)
- .limit(10)
- .sort('-age')
- .exec().then( docs => {
- console.dir(docs);
- });
- ```
Encryption
By setting a schema-field to `encrypted`, the value of this field will be stored in encryption-mode and can't be read without the password. Of course you can also encrypt nested objects. Example:
- ``` json
- {
- "title": "my schema",
- "properties": {
- "secret": {
- "type": "string",
- "encrypted": true
- }
- },
- "encrypted": [
- "secret"
- ]
- }
- ```
Import / Export
RxDB lets you import and export the whole database or single collections into json-objects. This is helpful to trace bugs in your application or to move to a given state in your tests.
- ``` js
- // export a single collection
- const jsonCol = await myCollection.dump();
- // export the whole database
- const jsonDB = await myDatabase.dump();
- // import the dump to the collection
- await emptyCollection.importDump(json);
- // import the dump to the database
- await emptyDatabase.importDump(json);
- ```
Key-Compression
Depending on which adapter and in which environment you use RxDB, client-side storage is [limited](https://pouchdb.com/2014/10/26/10-things-i-learned-from-reading-and-writing-the-pouchdb-source.html) in some way or the other. To save disc-space, RxDB uses a schema based [keycompression](https://github.com/pubkey/jsonschema-key-compression) to minimize the size of saved documents. This saves about 40% of used storage.
- ``` js
- // when you save an object with big keys
- await myCollection.insert({
- firstName: 'foo'
- lastName: 'bar'
- stupidLongKey: 5
- });
- // key compression will internally transform it to
- {
- '|a': 'foo'
- '|b': 'bar'
- '|c': 5
- }
- // so instead of 46 chars, the compressed-version has only 28
- // the compression works internally, so you can of course still access values via the original key.names and run normal queries.
- console.log(myDoc.firstName);
- // 'foo'
- ```