SDK
Quickstart
Run a short script against an in-memory replica: read, watch, write and see a stale write refused.
This script needs no account, no service and no collection. It uses
MemoryReplica, an in-memory replica from
@mdbase-dev/sdk/testing that speaks the same protocol as a
real one. Your code talks to it through the same
MdbaseClient it would use with a real collection.
Prerequisites
- Node 20 or later.
- npm.
1. Install the SDK
mkdir mdbase-quickstart && cd mdbase-quickstart
npm init -y
npm install @mdbase-dev/sdk
The package is not on npm yet.
Until @mdbase-dev/sdk is published,
npm install fails. This page will give the exact version
once it is published.
2. Run the example
Save this as quickstart.mjs and run
node quickstart.mjs. It prints its results and exits.
import { connect, isMdbaseError, summarizeStatus } from "@mdbase-dev/sdk";
import { MemoryReplica } from "@mdbase-dev/sdk/testing";
// An in-memory stand-in for a replica. Writes stay pending until confirmAll().
const replica = new MemoryReplica({ confirmDelayMs: null });
replica.seed({
path: "tasks/first.md",
types: ["task"],
frontmatter: { title: "Read the collection", status: "open" },
});
const db = await connect({
connector: replica.connector(),
app: { name: "dev.example.quickstart", version: "0.1.0" },
});
// A live query: a snapshot first, then a diff for every change.
const tasks = db.live({
types: ["task"],
order_by: [{ field: "file.path", direction: "asc" }],
});
await tasks.ready;
console.log(`Tasks: ${tasks.records.map((r) => r.path).join(", ")}`);
// Writes return at once with the optimistic result.
const created = await db.create({
type: "task",
path: "tasks/second.md",
frontmatter: { title: "Update one record", status: "open" },
});
console.log(`Created: ${created.records[0].path} (${created.state})`);
console.log(`Status: ${summarizeStatus(await db.getStatus()).text}`);
// Pass the record you read, so only the fields you change are sent.
const first = await db.get({ path: "tasks/first.md" });
const done = await db.update(first, { patch: { status: "done" } });
replica.confirmAll(); // The log accepts both writes.
const receipt = await done.confirmed;
console.log(`Confirmed at position ${receipt.seq}`);
// A whole-record check fails if the record changed since you read it.
try {
const stale = await db.update(first, { patch: { status: "open" }, ifRevision: first.revision });
await stale.confirmed;
} catch (e) {
if (!isMdbaseError(e, "conflict")) throw e;
console.log(`Stale revision: ${e.code} (${e.reason})`);
}
console.log(`Status: ${summarizeStatus(await db.getStatus()).text}`);
console.log(`Open tasks: ${tasks.records.filter((r) => r.frontmatter.get("status") === "open").length}`);
tasks.close();
db.close();
3. Check the result
Tasks: tasks/first.md
Created: tasks/second.md (pending)
Status: Synced through 1, 1 change waiting
Confirmed at position 3
Stale revision: conflict (revision)
Status: Synced through 3
Open tasks: 1
4. What it shows
-
Writes are optimistic.
create()andupdate()return aWritestraight away, in thependingstate. Your reads and live queries already show the change.write.confirmedresolves when the collection’s log accepts it. -
Live queries stay current.
db.live()keepsrecordsup to date as writes land, so the count at the end already includes both changes. -
Frontmatter is a
Map. Read fields withrecord.frontmatter.get("status"), or convert withtoPlain(). -
Stale writes are refused. With
ifRevision, a write against an old revision fails with the error codeconflictand reasonrevision.
MemoryReplica is not the real replica. It applies patches
blindly and has no query planner or merge. Use it for unit tests and demo
modes, not to test merge behaviour.