A local-first, reactive database for JavaScript & TypeScript.
Works in browsers, Node.js, and edge runtimes — no native dependencies.
import { RealDB } from '@waelio/realdb'
interface Task { title: string; done: boolean }
const db = new RealDB({ name: 'myApp' })
await db.open()
const tasks = db.collection<Task>('tasks')
const task = await tasks.insert({ title: 'Build realdb', done: false })
await tasks.update(task.id, { done: true })
tasks.subscribe((event) => {
console.log(event.type, event.document)
})
- 🔷 Fully typed — generics flow end-to-end, from schema to query results
- ⚡ Reactive — subscribe to collection changes with
collection.subscribe()
- 🔌 Pluggable adapters — swap storage without changing your app code
- 🧪 Testable —
MemoryAdapter is the default, perfect for unit tests
- 🌐 Universal — runs anywhere that speaks ES2020+
npm install @waelio/realdb
import { RealDB, LocalStorageAdapter } from '@waelio/realdb'
interface Note { text: string; pinned: boolean }
// Use LocalStorage in the browser
const db = new RealDB({
name: 'notes-app',
adapter: new LocalStorageAdapter('notes-app'),
})
await db.open()
const notes = db.collection<Note>('notes')
// Insert
const note = await notes.insert({ text: 'Hello realdb', pinned: false })
// Find with filters
const pinned = await notes.find({
filter: [{ field: 'pinned', op: 'eq', value: true }],
sort: [{ field: 'createdAt', direction: 'desc' }],
limit: 10,
})
// Update
await notes.update(note.id, { pinned: true })
// Delete
await notes.delete(note.id)
// Subscribe to changes
const sub = notes.subscribe((event) => {
console.log(`[${event.type}]`, event.document)
})
sub.unsubscribe() // stop listening
| Adapter |
Import |
Persists? |
Best For |
MemoryAdapter |
'realdb' |
❌ No |
Tests, SSR, edge runtimes |
LocalStorageAdapter |
'realdb' |
✅ Yes |
Browser apps |
Bring your own by implementing the StorageAdapter interface:
import type { StorageAdapter } from '@waelio/realdb'
class MyAdapter implements StorageAdapter {
name = 'my-adapter'
async init(collection: string) { /* ... */ }
async getAll(collection: string) { /* ... */ }
async getById(collection: string, id: string) { /* ... */ }
async put(collection: string, doc: unknown) { /* ... */ }
async delete(collection: string, id: string) { /* ... */ }
async clear(collection: string) { /* ... */ }
async destroy() { /* ... */ }
}
await tasks.find({
filter: [
{ field: 'done', op: 'eq', value: false },
{ field: 'priority', op: 'gte', value: 2 },
{ field: 'title', op: 'contains', value: 'realdb' },
],
})
| Operator |
Description |
eq / neq |
Equal / not equal |
gt / gte / lt / lte |
Numeric comparisons |
in / nin |
Value in / not in array |
contains |
String contains |
startsWith / endsWith |
String prefix / suffix |
await tasks.find({
sort: [{ field: 'priority', direction: 'desc' }],
limit: 20,
offset: 40,
})
const sub = tasks.subscribe((event) => {
// event.type → 'insert' | 'update' | 'delete'
// event.document → the new/current document
// event.previous → the old document (update/delete only)
})
sub.unsubscribe()
| Method / Property |
Description |
new RealDB(config) |
Create a database instance |
db.open() |
Open the database (call once) |
db.close() |
Shut down and free resources |
db.collection<T>(name, schema?) |
Get or create a collection |
db.collectionNames |
Array of registered collection names |
db.isOpen |
Whether the DB is open |
db.name |
Database name |
db.adapterName |
Active adapter's name |
| Method |
Description |
insert(data) |
Create a new document |
insertMany(items) |
Create multiple documents |
findById(id) |
Find one by ID |
find(options?) |
Find with filter/sort/limit/offset |
findAll() |
All documents, newest first |
count(options?) |
Count matching documents |
update(id, patch) |
Partial update |
replace(id, data) |
Full replace |
delete(id) |
Delete one |
deleteMany(options?) |
Delete matching |
clear() |
Wipe collection |
subscribe(callback) |
Watch for changes |
MIT © Peace Marshal