Skip to content

Commit e90bec1

Browse files
pubkeyKyleAMathews
andauthored
ADD RxDB Collections (#468)
Co-authored-by: Kyle Mathews <[email protected]>
1 parent b5d4210 commit e90bec1

File tree

17 files changed

+2236
-16
lines changed

17 files changed

+2236
-16
lines changed

.github/workflows/pr.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ name: PR
33
on:
44
pull_request:
55

6+
# Allows you to run this workflow manually from the Actions tab
7+
workflow_dispatch:
8+
69
concurrency:
710
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
811
cancel-in-progress: true

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ TanStack DB provides several collection types to support different backend integ
144144
- **`@tanstack/query-db-collection`** - Collections backed by [TanStack Query](https://tanstack.com/query) for REST APIs and GraphQL endpoints
145145
- **`@tanstack/electric-db-collection`** - Real-time sync collections powered by [ElectricSQL](https://electric-sql.com) for live database synchronization
146146
- **`@tanstack/trailbase-db-collection`** - Collections for [TrailBase](https://trailbase.io) backend integration
147+
- **`@tanstack/rxdb-db-collection`** - Collections backed by [RxDB](https://rxdb.info), a client-side database with replication support and local persistence made for local-first apps.
147148

148149
## Framework integrations
149150

@@ -157,7 +158,7 @@ TanStack DB integrates with React & Vue with more on the way!
157158
```bash
158159
npm install @tanstack/react-db
159160
# Optional: for specific collection types
160-
npm install @tanstack/electric-db-collection @tanstack/query-db-collection
161+
npm install @tanstack/electric-db-collection @tanstack/query-db-collection @tanstack/trailbase-db-collection @tanstack/rxdb-db-collection
161162
```
162163

163164
Other framework integrations are in progress.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
title: RxDB Collection
3+
---
4+
5+
# RxDB Collection
6+
7+
RxDB collections provide seamless integration between TanStack DB and [RxDB](https://rxdb.info), enabling automatic synchronization between your in-memory TanStack DB collections and RxDB's local-first database. Giving you offline-ready persistence, and powerful sync capabilities with a wide range of backends.
8+
9+
10+
## Overview
11+
12+
The `@tanstack/rxdb-db-collection` package allows you to create collections that:
13+
- Automatically mirror the state of an underlying RxDB collection
14+
- Reactively update when RxDB documents change
15+
- Support optimistic mutations with rollback on error
16+
- Provide persistence handlers to keep RxDB in sync with TanStack DB transactions
17+
- Sync across browser tabs - changes in one tab are reflected in RxDB and TanStack DB collections in all tabs
18+
- Use one of RxDB's [storage engines](https://rxdb.info/rx-storage.html).
19+
- Work with RxDB's [replication features](https://rxdb.info/replication.html) for offline-first and sync scenarios
20+
- Leverage RxDB's [replication plugins](https://rxdb.info/replication.html) to sync with CouchDB, MongoDB, Supabase, REST APIs, GraphQL, WebRTC (P2P) and more.
21+
22+
23+
## 1. Installation
24+
25+
Install the RXDB collection packages along with your preferred framework integration.
26+
27+
```bash
28+
npm install @tanstack/rxdb-db-collection rxdb @tanstack/react-db
29+
```
30+
31+
32+
### 2. Create an RxDatabase and RxCollection
33+
34+
```ts
35+
import { createRxDatabase, addRxPlugin } from 'rxdb/plugins/core'
36+
37+
/**
38+
* Here we use the localstorage based storage for RxDB.
39+
* RxDB has a wide range of storages based on Dexie.js, IndexedDB, SQLite and more.
40+
*/
41+
import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage'
42+
43+
// add json-schema validation (optional)
44+
import { wrappedValidateAjvStorage } from 'rxdb/plugins/validate-ajv';
45+
46+
// Enable dev mode (optional, recommended during development)
47+
import { RxDBDevModePlugin } from 'rxdb/plugins/dev-mode'
48+
addRxPlugin(RxDBDevModePlugin)
49+
50+
type Todo = { id: string; text: string; completed: boolean }
51+
52+
const db = await createRxDatabase({
53+
name: 'my-todos',
54+
storage: wrappedValidateAjvStorage({
55+
storage: getRxStorageLocalstorage()
56+
})
57+
})
58+
59+
await db.addCollections({
60+
todos: {
61+
schema: {
62+
title: 'todos',
63+
version: 0,
64+
type: 'object',
65+
primaryKey: 'id',
66+
properties: {
67+
id: { type: 'string', maxLength: 100 },
68+
text: { type: 'string' },
69+
completed: { type: 'boolean' },
70+
},
71+
required: ['id', 'text', 'completed'],
72+
},
73+
},
74+
})
75+
```
76+
77+
78+
### 3. (optional) sync with a backend
79+
```ts
80+
import { replicateRxCollection } from 'rxdb/plugins/replication'
81+
const replicationState = replicateRxCollection({
82+
collection: db.todos,
83+
pull: { handler: myPullHandler },
84+
push: { handler: myPushHandler },
85+
})
86+
```
87+
88+
### 4. Wrap the RxDB collection with TanStack DB
89+
90+
```ts
91+
import { createCollection } from '@tanstack/react-db'
92+
import { rxdbCollectionOptions } from '@tanstack/rxdb-db-collection'
93+
94+
const todosCollection = createCollection(
95+
rxdbCollectionOptions({
96+
rxCollection: myDatabase.todos,
97+
startSync: true, // start ingesting RxDB data immediately
98+
})
99+
)
100+
```
101+
102+
103+
Now `todosCollection` is a reactive TanStack DB collection driven by RxDB:
104+
105+
- Writes via `todosCollection.insert/update/delete` persist to RxDB.
106+
- Direct writes in RxDB (or via replication) flow into the TanStack collection via change streams.
107+
108+
109+
110+
## Configuration Options
111+
112+
The `rxdbCollectionOptions` function accepts the following options:
113+
114+
### Required
115+
116+
- `rxCollection`: The underlying [RxDB collection](https://rxdb.info/rx-collection.html)
117+
118+
### Optional
119+
120+
- `id`: Unique identifier for the collection
121+
- `schema`: Schema for validating items. RxDB already has schema validation but having additional validation on the TanStack DB side can help to unify error handling between different tanstack collections.
122+
- `startSync`: Whether to start syncing immediately (default: true)
123+
- `onInsert, onUpdate, onDelete`: Override default persistence handlers. By default, TanStack DB writes are persisted to RxDB using bulkUpsert, patch, and bulkRemove.
124+
- `syncBatchSize`: The maximum number of documents fetched per batch during the initial sync from RxDB into TanStack DB (default: 1000). Larger values reduce round trips but use more memory; smaller values are lighter but may increase query calls. Note that this only affects the initial sync. Ongoing live updates are streamed one by one via RxDB's change feed.
125+
126+
127+
128+
## Syncing with Backends
129+
130+
Replication and sync in RxDB run independently of TanStack DB. You set up replication directly on your RxCollection using RxDB's replication plugins (for CouchDB, GraphQL, WebRTC, REST APIs, etc.).
131+
132+
When replication runs, it pulls and pushes changes to the backend and applies them to the RxDB collection. Since the TanStack DB integration subscribes to the RxDB change stream, any changes applied by replication are automatically reflected in your TanStack DB collection.
133+
134+
This separation of concerns means you configure replication entirely in RxDB, and TanStack DB automatically benefits: your TanStack collections always stay up to date with whatever sync strategy you choose.

docs/config.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@
8888
{
8989
"label": "Electric Collection",
9090
"to": "collections/electric-collection"
91+
},
92+
{
93+
"label": "RxDB Collection",
94+
"to": "collections/rxdb-collection"
9195
}
9296
]
9397
},
@@ -137,6 +141,14 @@
137141
{
138142
"label": "queryCollectionOptions",
139143
"to": "reference/query-db-collection/functions/querycollectionoptions"
144+
},
145+
{
146+
"label": "RxDB DB Collection",
147+
"to": "reference/rxdb-db-collection/index"
148+
},
149+
{
150+
"label": "rxdbCollectionOptions",
151+
"to": "reference/rxdb-db-collection/functions/rxdbcollectionoptions"
140152
}
141153
],
142154
"frameworks": [

docs/guides/collection-options-creator.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Collection options creators follow a consistent pattern:
1717
## When to Create a Custom Collection
1818

1919
You should create a custom collection when:
20-
- You have a dedicated sync engine (like ElectricSQL, Trailbase, Firebase, or a custom WebSocket solution)
20+
- You have a dedicated sync engine (like ElectricSQL, Trailbase, Firebase, RxDB or a custom WebSocket solution)
2121
- You need specific sync behaviors that aren't covered by the query collection
2222
- You want to integrate with a backend that has its own sync protocol
2323

@@ -331,6 +331,7 @@ For complete, production-ready examples, see the collection packages in the TanS
331331
- **[@tanstack/query-collection](https://github.com/TanStack/db/tree/main/packages/query-collection)** - Pattern A: User-provided handlers with full refetch strategy
332332
- **[@tanstack/trailbase-collection](https://github.com/TanStack/db/tree/main/packages/trailbase-collection)** - Pattern B: Built-in handlers with ID-based tracking
333333
- **[@tanstack/electric-collection](https://github.com/TanStack/db/tree/main/packages/electric-collection)** - Pattern A: Transaction ID tracking with complex sync protocols
334+
- **[@tanstack/rxdb-collection](https://github.com/TanStack/db/tree/main/packages/rxdb-collection)** - Pattern B: Built-in handlers that bridge [RxDB](https://rxdb.info) change streams into TanStack DB's sync lifecycle
334335

335336
### Key Lessons from Production Collections
336337

@@ -349,6 +350,11 @@ For complete, production-ready examples, see the collection packages in the TanS
349350
- Demonstrates advanced deduplication techniques
350351
- Shows how to wrap user handlers with sync coordination
351352

353+
**From RxDB Collection:**
354+
- Uses RxDB's built-in queries and change streams
355+
- Uses `RxCollection.$` to subscribe to inserts/updates/deletes and forward them to TanStack DB with begin-write-commit
356+
- Implements built-in mutation handlers (onInsert, onUpdate, onDelete) that call RxDB APIs (bulkUpsert, incrementalPatch, bulkRemove)
357+
352358
## Complete Example: WebSocket Collection
353359

354360
Here's a complete example of a WebSocket-based collection options creator that demonstrates the full round-trip flow:

docs/installation.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,14 @@ npm install @tanstack/trailbase-db-collection
8585
```
8686

8787
Use `trailBaseCollectionOptions` to sync records from TrailBase's Record APIs with built-in subscription support.
88+
89+
### RxDB Collection
90+
91+
For offline-first apps and local persistence with [RxDB](https://rxdb.info):
92+
93+
```sh
94+
npm install @tanstack/rxdb-db-collection
95+
```
96+
97+
Use `rxdbCollectionOptions` to bridge an [RxDB collection](https://rxdb.info/rx-collection.html) into TanStack DB.
98+
This gives you reactive TanStack DB collections backed by RxDB's powerful local-first database, replication, and conflict handling features.

docs/overview.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,9 @@ There are a number of built-in collection types:
154154
1. [`QueryCollection`](#querycollection) to load data into collections using [TanStack Query](https://tanstack.com/query)
155155
2. [`ElectricCollection`](#electriccollection) to sync data into collections using [ElectricSQL](https://electric-sql.com)
156156
3. [`TrailBaseCollection`](#trailbasecollection) to sync data into collections using [TrailBase](https://trailbase.io)
157-
4. [`LocalStorageCollection`](#localstoragecollection) for small amounts of local-only state that syncs across browser tabs
158-
5. [`LocalOnlyCollection`](#localonlycollection) for in-memory client data or UI state
157+
4. [`RxDBCollection`](#rxdbcollection) to integrate with [RxDB](https://rxdb.info) for local persistence and sync
158+
5. [`LocalStorageCollection`](#localstoragecollection) for small amounts of local-only state that syncs across browser tabs
159+
6. [`LocalOnlyCollection`](#localonlycollection) for in-memory client data or UI state
159160

160161
You can also use:
161162

@@ -301,6 +302,52 @@ This collection requires the following TrailBase-specific options:
301302
A new collections doesn't start syncing until you call `collection.preload()` or you query it.
302303

303304

305+
#### `RxDBCollection`
306+
307+
[RxDB](https://rxdb.info) is a client-side database for JavaScript apps with replication, conflict resolution, and offline-first features.
308+
Use `rxdbCollectionOptions` from `@tanstack/rxdb-db-collection` to integrate an RxDB collection with TanStack DB:
309+
310+
```ts
311+
import { createCollection } from "@tanstack/react-db"
312+
import { rxdbCollectionOptions } from "@tanstack/rxdb-db-collection"
313+
import { createRxDatabase } from "rxdb"
314+
315+
const db = await createRxDatabase({
316+
name: "mydb",
317+
storage: getRxStorageMemory(),
318+
})
319+
await db.addCollections({
320+
todos: {
321+
schema: {
322+
version: 0,
323+
primaryKey: "id",
324+
type: "object",
325+
properties: {
326+
id: { type: "string", maxLength: 100 },
327+
text: { type: "string" },
328+
completed: { type: "boolean" },
329+
},
330+
},
331+
},
332+
})
333+
334+
// Wrap the RxDB collection with TanStack DB
335+
export const todoCollection = createCollection(
336+
rxdbCollectionOptions({
337+
rxCollection: db.todos,
338+
startSync: true
339+
})
340+
)
341+
```
342+
343+
With this integration:
344+
345+
- TanStack DB subscribes to RxDB's change streams and reflects updates, deletes, and inserts in real-time.
346+
- You get local-first sync when RxDB replication is configured.
347+
- Mutation handlers (onInsert, onUpdate, onDelete) are implemented using RxDB's APIs (bulkUpsert, incrementalPatch, bulkRemove).
348+
349+
This makes RxDB a great choice for apps that need local-first storage, replication, or peer-to-peer sync combined with TanStack DB's live queries and transaction lifecycle.
350+
304351
#### `LocalStorageCollection`
305352

306353
localStorage collections store small amounts of local-only state that persists across browser sessions and syncs across browser tabs in real-time. All data is stored under a single localStorage key and automatically synchronized using storage events.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# @tanstack/rxdb-db-collection
2+
3+
## 0.0.0
4+
5+
- Initial Release
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"name": "@tanstack/rxdb-db-collection",
3+
"description": "RxDB collection for TanStack DB",
4+
"version": "0.1.4",
5+
"dependencies": {
6+
"rxdb": "16.17.2",
7+
"@standard-schema/spec": "^1.0.0",
8+
"@tanstack/db": "workspace:*",
9+
"@tanstack/store": "^0.7.0",
10+
"debug": "^4.4.1"
11+
},
12+
"devDependencies": {
13+
"@types/debug": "^4.1.12",
14+
"@vitest/coverage-istanbul": "^3.0.9"
15+
},
16+
"exports": {
17+
".": {
18+
"import": {
19+
"types": "./dist/esm/index.d.ts",
20+
"default": "./dist/esm/index.js"
21+
},
22+
"require": {
23+
"types": "./dist/cjs/index.d.cts",
24+
"default": "./dist/cjs/index.cjs"
25+
}
26+
},
27+
"./package.json": "./package.json"
28+
},
29+
"files": [
30+
"dist",
31+
"src"
32+
],
33+
"main": "dist/cjs/index.cjs",
34+
"module": "dist/esm/index.js",
35+
"packageManager": "[email protected]",
36+
"peerDependencies": {
37+
"rxdb": ">=16.17.2",
38+
"typescript": ">=4.7"
39+
},
40+
"author": "Kyle Mathews",
41+
"license": "MIT",
42+
"repository": {
43+
"type": "git",
44+
"url": "https://github.com/TanStack/db.git",
45+
"directory": "packages/rxdb-db-collection"
46+
},
47+
"homepage": "https://tanstack.com/db",
48+
"keywords": [
49+
"rxdb",
50+
"nosql",
51+
"realtime",
52+
"local-first",
53+
"sync-engine",
54+
"sync",
55+
"replication",
56+
"opfs",
57+
"indexeddb",
58+
"localstorage",
59+
"optimistic",
60+
"typescript"
61+
],
62+
"scripts": {
63+
"build": "vite build",
64+
"dev": "vite build --watch",
65+
"lint": "eslint . --fix",
66+
"test": "npx vitest --run"
67+
},
68+
"sideEffects": false,
69+
"type": "module",
70+
"types": "dist/esm/index.d.ts"
71+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const RESERVED_RXDB_FIELDS = new Set([
2+
`_rev`,
3+
`_deleted`,
4+
`_attachments`,
5+
`_meta`,
6+
])
7+
8+
export function stripRxdbFields<T extends Record<string, any>>(
9+
obj: T | any
10+
): T {
11+
if (!obj) return obj
12+
const out: any = Array.isArray(obj) ? [] : {}
13+
for (const k of Object.keys(obj)) {
14+
if (RESERVED_RXDB_FIELDS.has(k)) continue
15+
out[k] = obj[k]
16+
}
17+
return out as T
18+
}

0 commit comments

Comments
 (0)