Document
memory, semantic (vector) recall, full-text search, a durabletask queue, atomiclocks, cache, andcron— over a single SQLite file, with a zero-dependency TypeScript core.No Docker. No connection strings. Nothing to run.And thesame codescales to Postgres the day you need it.
A coding agent, RAG pipeline, or autonomous worker needs memory, semantic search, a job queue, and locks. That's normally MongoDB + Qdrant + Redis + BullMQ — four services and a Docker compose file. monlite is all of it, in a file you can cp to back up:
import { createDb } from "@monlite/core";
import { vector } from "@monlite/vector";
import { createQueue } from "@monlite/queue";
import { kv } from "@monlite/kv";
// one file = the agent's entire backend
const db = createDb("./agent.db", {
allowExtensions: true,
plugins: [vector({ memory: { field: "embedding", dimensions: 384 } })],
});
// 🧠 memory — typed document collections
await db.collection("memory").create({ data: { note: "user prefers dark mode", embedding } });
// 🔎 semantic recall — vector search over embeddings
const recall = await db.collection("memory").findSimilar({ vector: query, topK: 5 });
// 🔒 an atomic lock (run-once) + 📋 a durable task queue (retries, backoff, dedupe, concurrency)
if (kv(db).setNX("lock:ingest", 1, { ttl: 30_000 })) startIngest();
createQueue(db).process("embed", (job) => embed(job.payload.text), { concurrency: 4 });
Exactly-once job claims, locks, scheduling, and full-text + semantic search — with no server, no
migrations, and no native build (Node 22.5+ uses the built-in node:sqlite
).
📖 Docs · 🎮 Live demo (runs in your browser) · 📦 npm · 💻 GitHub
Most apps, CLIs, and AI agents wire up the same services. monlite gives you each one as a small
package over a single .db
file — install only what you use, the core stays zero-dependency:
| Instead of | Use | Gives you |
|---|---|---|
| MongoDB / Mongoose | ||
@monlite/core |
watch()
@monlite/fts
collection.search()
@monlite/vector
findSimilar()
, hybrid RAG@monlite/kv
@monlite/queue
@monlite/cron
@monlite/realtime
@monlite/sync
@monlite/postgres
the same API on a networked Postgres when you outgrow one fileNo Docker. No .env
full of connection strings. One file, one API, node serve.mjs
.
Batteries-included — the whole stack in one package:
npm install monlite
js
import { createDb, kv, createQueue, createCron, fts, vector } from "monlite";
Or the minimal, zero-dependency core, plus packages à la carte:
npm install @monlite/core # zero-dep core (Node ≥ 22.5, built-in node:sqlite)
npm install @monlite/core better-sqlite3 # Node 18/20, or to skip the experimental flag
npm install @monlite/fts # full-text search @monlite/vector # semantic search
npm install @monlite/kv # cache, locks, pub/sub @monlite/queue # durable job queue
npm install @monlite/cron # scheduler @monlite/realtime # live queries over SSE
npm install @monlite/postgres # run the same API on Postgres
npm install @monlite/sync # cloud sync (MongoDB / PostgreSQL / MySQL)
npm install @monlite/wasm # browser / SQLite-WASM @monlite/electron # Electron main↔renderer
Zero-install inspector: ** npx @monlite/studio app.db** opens a local web UI to browse collections, view documents, and run queries.
A Mongo/Prisma-style API. Typed collections get compile-time-checked where
/orderBy
, and return
types that narrow with select
.
interface Order {
customerId: string;
items: { sku: string; qty: number }[];
status: "pending" | "shipped" | "returned";
total: number;
}
const orders = db.collection<Order>("orders");
// query inside arrays of objects
await orders.findMany({ where: { items: { elemMatch: { sku: "WIDGET", qty: { gte: 5 } } } } });
// case-insensitive regex
await orders.findMany({ where: { status: { regex: "^pend", mode: "insensitive" } } });
// grouped aggregation — GROUP BY with sums, HAVING, top-N
await orders.groupBy({
by: ["customerId"],
where: { status: "shipped" },
_sum: { total: true },
orderBy: { _sum: { total: "desc" } },
take: 10,
});
// atomic transactions — await inside, all-or-nothing
await db.transactionAsync(async (tx) => {
const accounts = tx.collection("accounts");
await accounts.update({ where: { _id: "acc-1" }, data: { $inc: { balance: -100 } } });
await accounts.update({ where: { _id: "acc-2" }, data: { $inc: { balance: +100 } } });
});
// cross-process compare-and-swap — exactly-once job claim
const claimed = await orders.findOneAndUpdate({
where: { status: "pending" },
data: { $set: { status: "active" } },
returnDocument: "after",
}); // N workers race; exactly one wins, the rest get null
Full surface: create
/createMany
, findMany
/findFirst
/findById
, update
/updateMany
,
upsert
, delete
/deleteMany
, count
/exists
/distinct
, aggregate
/groupBy
, bulkWrite
,
findOneAndUpdate
, TTL collections, explain()
, and structured (columnar) collections.
collection.watch()
returns a live result set that re-emits only when a relevant change lands
(row-level matching — no spurious re-renders), with added
/removed
/changed
/moved
deltas.
// initial snapshot, then re-fires only when an admin is added/changed/removed
users.watch({ where: { roles: { has: "admin" } } }, ({ results, added, removed }) =>
renderAdminList(results),
);
// single-document listener (Firebase-style onSnapshot) — doc is null on delete
orders.watchDoc("o-123", (doc) => render(doc));
Enable the change feed ({ changefeed: true }
) for a durable, resumable, ordered stream — and
watch()
then also sees writes from other processes on the same file:
for await (const ev of db.changes("orders", { since: lastSeq })) {
// { seq, collection, id, op: "upsert" | "delete", ts } — resumable by seq
}
Add the plugins, point them at fields, and they index automatically on every write. Keyword ranking and vector similarity fuse into one ranked list via Reciprocal Rank Fusion.
import { fts } from "@monlite/fts";
import { vector, hybridSearch } from "@monlite/vector";
const db = createDb("./app.db", {
allowExtensions: true,
plugins: [
fts({ docs: ["title", "body"] }),
vector({ docs: { field: "embedding", dimensions: 384 } }),
],
});
await db.collection("docs").search("brown fox"); // keyword (FTS5)
await db.collection("docs").findSimilar({ vector: emb, topK: 5 }); // semantic (sqlite-vec)
const hits = await hybridSearch(db.collection("docs"), { // both, fused
text: "machine learning", vector: await embed("machine learning"),
topK: 10, where: { published: true },
});
Indexing is linear at scale — verified ingesting 100K documents in ~0.8s and 50K vectors in ~8s (no O(n²) re-index), comfortably backing a 10K–100K-document RAG corpus.
import { kv } from "@monlite/kv";
import { createQueue } from "@monlite/queue";
import { createCron } from "@monlite/cron";
// Redis-like cache: get/set with TTL, atomic locks, counters, sorted sets, pub/sub
const cache = kv(db);
cache.set("session:42", { user: "ali" }, { ttl: 60_000 });
if (cache.setNX("lock:job:42", 1, { ttl: 30_000 })) runOnce(); // atomic lock
// durable job queue: retries, backoff, dedupe, concurrency, rate limits
const queue = createQueue(db, { maxAttempts: 3 });
queue.process("embed", async (job) => embed(job.payload.text), { concurrency: 4 });
// persisted scheduler: 5-field cron, time zones, jitter, multi-process safe
const cron = createCron(db);
cron.schedule("nightly", "0 3 * * *", () => queue.add("cleanup", {}));
The full AI-agent-backend walkthrough puts these together with memory + semantic recall into one runtime.
The collection API is engine-agnostic. Develop against a local .db
; when you need a networked, multi-writer backend, swap the engine, not your app:
import { createDb } from "@monlite/core"; const db = createDb("app.db"); // local
import { createDb } from "@monlite/postgres"; const db = createDb("postgres://…"); // server
@monlite/postgres runs the
entire surface on Postgres (documents as JSONB): all CRUD, the full query language,
aggregate
/groupBy
,
explain()
, realtime watch()
over LISTEN/NOTIFY
(truly cross-process), full-text search
(tsvector
), vector search (pgvector), the job queue (SKIP LOCKED
), cache, and cron — the sameplugins and the same calls. A ready-to-run
Docker image bundles Postgres 16 + pgvector, preconfigured.
monlite/postgres
| Environment | How |
|---|---|
| Node 22.5+ | @monlite/core — built-in node:sqlite , zero native build |
| Node 18/20 | @monlite/core + better-sqlite3 (auto-selected when present) |
| Browser | @monlite/wasm — same API on SQLite-WASM |
| Electron | @monlite/electron — DB in main, same API in renderers over IPC |
| Python | pip install monlite — the same , pure stdlib.db file |
The Python port is at feature parity — documents (transactions, aggregation, change feed), kv, queue, cron, FTS5, and vector search — reading and writing the same file as the Node packages, with a cross-runtime interop suite round-tripping a database between them. So Python ingests/embeds while Node serves, over one file.
from monlite import create_db, kv
db = create_db("app.db") # the same file your Node process uses
db.collection("users").find_many(where={"tags": {"has": "admin"}})
kv(db).set("session:42", {"user": "ali"}, ttl=60_000)
vs. raw SQLite— you'd hand-write the document layer, query translator, FTS/vector wiring, change feed, sync engine, and all the types. monlite is that work, done and tested.vs. MongoDB + Redis + Qdrant— for local / edge / desktop / single-machine work you'd run three services to solve one problem. monlite is one file, one API, zero infrastructure — and scales to Postgres with the same code when you genuinely need a server.vs. Firebase / Supabase— great for shared cloud state, awkward when you need to work offline, ship a CLI, or keep data on-device. monlite is local-first;@monlite/sync
adds the cloud when you want it.
Full guide at ** qataruts.github.io/monlite**:
Getting started·** Core**—documents·queries·aggregation·realtime·transactionsPackages—postgres·fts·vector·kv·queue·cron·syncGuides—AI-agent backend·production·migrationsReference—file format·Python·benchmarks
Runnable demos in examples/. The
live demoruns every package — documents, FTS5, vector search, cache, queue, cron — 100% in the browser on SQLite-WASM, with embeddings computed on-device via Transformers.js.
Production-ready and published; the 2.x core API is frozen. The Postgres engine ( @monlite/postgres) runs the entire surface — documents, queries, aggregation, realtime, full-text, vector, queue, kv, and cron — verified against live Postgres. See each package on npm for its current version and changelog.
MIT