@moltendb-web/query
v0.4.0
Published
Type-safe query builder for MoltenDB — chainable API for get, set, update and delete operations.
Maintainers
Readme
@moltendb-web/query
Type-safe, chainable query builder for MoltenDB.
Works in vanilla JavaScript and TypeScript. Compiles as an npm module (CJS + ESM + .d.ts).
🌋 Explore the Full Functionality
The best way to experience the MoltenDB query builder is through our Interactive Demo on StackBlitz. It contains a complete, live environment where you can test query builder expressions, perform mutations, and see real-time events without any local setup.
Installation
npm install @moltendb-web/queryQuick start
import { MoltenDB } from '@moltendb-web/core';
import { MoltenDBClient, WorkerTransport } from '@moltendb-web/query';
// 1. Initialize the Core Engine (boots WASM worker)
const workerUrl = new URL('@moltendb-web/core/worker', import.meta.url).href;
const db = new MoltenDB('moltendb_demo', { syncEnabled: false, workerUrl });
await db.init();
// 2. Connect the Query Builder to the worker
const client = new MoltenDBClient(new WorkerTransport(db.worker, 10000));
// SET — insert / upsert
await client.collection('laptops')
.set({
lp1: { brand: 'Lenovo', model: 'ThinkPad X1', price: 1499, in_stock: true },
lp2: { brand: 'Apple', model: 'MacBook Pro', price: 3499, in_stock: true },
})
.exec();
// GET — query with WHERE, field projection, sort and pagination
const results = await client.collection('laptops')
.get()
.where({ brand: 'Apple' })
.fields(['brand', 'model', 'price'])
.sort([{ field: 'price', order: 'asc' }])
.count(5)
.exec();
// UPDATE — partial patch (only listed fields are changed)
await client.collection('laptops')
.update({ lp1: { price: 1749, in_stock: false } })
.exec();
// DELETE — single key
await client.collection('laptops').delete().keys('lp1').exec();
// DELETE — batch
await client.collection('laptops').delete().keys(['lp1', 'lp2']).exec();
// DELETE — drop entire collection
await client.collection('laptops').delete().drop().exec();Operations & allowed fields
Each operation only exposes the methods that are valid for it — invalid combinations are caught at compile time by TypeScript.
get() — read / query
| Method | Description |
|---|---|
| .keys(key \| key[]) | Fetch one or more documents by key |
| .where(clause) | Filter with operators: $eq $ne $gt $gte $lt $lte $in $nin $contains (and aliases) |
| .fields(string[]) | Return only these fields (dot-notation supported) |
| .excludedFields(string[]) | Return everything except these fields |
| .joins(JoinSpec[]) | Embed related documents from other collections |
| .sort(SortSpec[]) | Sort results (multi-field, asc/desc) |
| .count(n) | Limit results to N documents |
| .offset(n) | Skip first N results (pagination) |
| .build() | Return the raw JSON payload without sending |
| .exec() | Send the query and return the result |
set(data) — insert / upsert
| Method | Description |
|---|---|
| .extends(map) | Embed snapshots from other collections at insert time |
| .build() | Return the raw JSON payload without sending |
| .exec() | Send and return { count, status } |
data can be a { key: document } map or a Document[] array (UUIDv7 keys are auto-assigned for arrays).
update(data) — partial patch
| Method | Description |
|---|---|
| .build() | Return the raw JSON payload without sending |
| .exec() | Send and return { count, status } |
Only the fields present in each patch object are updated — all other fields are left unchanged.
delete() — delete documents or drop collection
| Method | Description |
|---|---|
| .keys(key \| key[]) | Delete one or more documents by key |
| .drop() | Drop the entire collection |
| .build() | Return the raw JSON payload without sending |
| .exec() | Send and return { count, status } |
WHERE operators
// Exact equality (implicit or explicit)
.where({ brand: 'Apple' })
.where({ brand: { $eq: 'Apple' } })
.where({ brand: { $equals: 'Apple' } }) // alias
// Comparison
.where({ price: { $gt: 1000, $lt: 3000 } })
.where({ price: { $greaterThan: 1000, $lessThan: 3000 } }) // aliases
.where({ 'specs.cpu.cores': { $gte: 12 } })
// Not equal
.where({ 'specs.cpu.brand': { $ne: 'Intel' } })
.where({ 'specs.cpu.brand': { $notEquals: 'Intel' } }) // alias
// In / not-in list
.where({ brand: { $in: ['Apple', 'Dell'] } })
.where({ brand: { $oneOf: ['Apple', 'Dell'] } }) // alias
.where({ brand: { $nin: ['Framework'] } })
.where({ brand: { $notIn: ['Framework'] } }) // alias
// Contains (string substring or array element)
.where({ model: { $contains: 'Pro' } })
.where({ model: { $ct: 'Pro' } }) // alias
.where({ tags: { $contains: 'gaming' } })
// Multiple conditions (implicit AND)
.where({ in_stock: true, 'specs.cpu.brand': 'Intel' })Joins
const results = await client.collection('laptops')
.get()
.fields(['brand', 'model', 'price'])
.joins([
{ alias: 'ram', from: 'memory', on: 'memory_id', fields: ['capacity_gb', 'type'] },
{ alias: 'screen', from: 'display', on: 'display_id', fields: ['refresh_hz', 'panel'] },
])
.exec();
// Each result: { brand, model, price, ram: { capacity_gb, type }, screen: { refresh_hz, panel } }Extends (snapshot embedding at insert time)
await client.collection('laptops')
.set({
lp7: {
brand: 'MSI', model: 'Titan GT77', price: 3299,
specs: { cpu: { brand: 'Intel', cores: 16, ghz: 5.0 } },
},
})
.extends({ ram: 'memory.mem4', screen: 'display.dsp3' })
.exec();
// lp7 is stored with the full mem4 and dsp3 documents embedded inline.When to use extends vs joins:
| | extends | joins |
|---|---|---|
| Resolved at | Insert time (once) | Query time (every request) |
| Data freshness | Snapshot — may become stale | Always live |
| Read cost | O(1) — data already embedded | O(1) per join per document |
| Use when | Data rarely changes, fast reads matter | Data changes frequently, freshness matters |
Custom transport
Implement MoltenTransport to connect to any backend:
import { MoltenTransport, MoltenDBClient, Document, JsonValue } from '@moltendb-web/query';
class FetchTransport implements MoltenTransport {
constructor(private baseUrl: string, private token: string) {}
async send(action: 'get' | 'set' | 'update' | 'delete', payload: Document): Promise<JsonValue> {
const res = await fetch(`${this.baseUrl}/${action}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`,
},
body: JSON.stringify(payload),
});
return res.json();
}
}
const db = new MoltenDBClient(new FetchTransport('https://localhost:1538', myToken));Build
npm run typecheck # type-check without emittingLicense
MIT OR Apache-2.0
