btrdb is a persistent, embedded NoSQL database engine written in TypeScript. It features a B-tree Copy-on-Write (CoW) architecture inspired by btrfs, enabling high-performance reads, crash safety, and instant point-in-time snapshots.
- Universal Runtime: Runs on Deno (native) and Node.js.
- Hybrid Storage: Supports both Key-Value sets and Document sets in the same database.
- Copy-on-Write (CoW): Never overwrites data. Ensures database integrity and enables instant snapshots.
- Time Travel: Create named snapshots and read data from any previous point in time.
- Advanced Querying: SQL-like tagged template queries or functional query builders.
- Efficient Indexing: Support for unique, compound, and computed indices.
- Transactions: ACID compliance with concurrent reader isolation.
- btrdbfs: Includes a FUSE filesystem implementation (mount your DB as a folder!).
β οΈ Warning: This project is under heavy development. The on-disk format and APIs are subject to change. Do not use in critical production environments yet.
Import directly from deno.land:
import { Database } from "https://deno.land/x/btrdb/mod.ts";Install via NPM:
npm install @yuuza/btrdbImport in your project:
import { Database } from "@yuuza/btrdb";
// or const { Database } = require("@yuuza/btrdb");import { Database } from "https://deno.land/x/btrdb/mod.ts"; // or @yuuza/btrdb
// 1. Open database (creates file if not exists)
const db = new Database();
await db.openFile("data.db");
// 2. Create a Document Set
const users = await db.createSet("users", "doc");
// 3. Insert Data
await users.insert({ username: "yuuza", role: "admin", active: true });
await users.insert({ username: "guest", role: "visitor", active: false });
// 4. Commit changes to disk
await db.commit();
// 5. Query Data
const admin = await users.query(query`role == ${"admin"}`);
console.log(admin);
db.close();Simple, persistent string-to-value storage.
const config = await db.createSet("config", "kv"); // 'kv' is default if omitted
// Set values
await config.set("theme", "dark");
await config.set("max_connections", 100);
// Get values
const theme = await config.get("theme"); // "dark"
// Iterate
await config.forEach((key, val) => {
console.log(`${key}: ${val}`);
});Store JSON-like objects with automatic IDs and powerful indexing.
interface User {
id: number;
username: string;
email: string;
age: number;
}
const users = await db.createSet<User>("users", "doc");
// Define Indices (Critical for query performance)
await users.useIndexes({
// Simple index
age: (u) => u.age,
// Unique index
username: { unique: true, key: (u) => u.username },
// Computed/Compound index
active_adult: (u) => u.age >= 18 && u.active,
});
// Upsert (Insert or Update by ID)
await users.upsert({ id: 1, username: "john", email: "[email protected]", age: 30 });btrdb provides a safe, tagged template literal syntax for queries.
Operators: ==, !=, >, <, <=, >=, AND, OR, NOT, SKIP, LIMIT.
import { query } from "@yuuza/btrdb";
// Find users older than 20 excluding specific IDs
const results = await users.query(query`
age > ${20}
AND NOT id == ${1}
LIMIT ${10}
`);
// You can also use functional builders
import { AND, EQ, GT } from "@yuuza/btrdb";
const results2 = await users.query(
AND(GT("age", 20), EQ("active", true))
);Transactions guarantee atomicity. If an error occurs, changes are rolled back.
await db.runTransaction(async () => {
const bank = await db.getSet("bank", "kv");
const balance = await bank.get("user_1");
if (balance < 100) throw new Error("Insufficient funds");
await bank.set("user_1", balance - 100);
await bank.set("user_2", (await bank.get("user_2")) + 100);
});
// Automatically commits here if successfulBecause btrdb is Copy-on-Write, creating snapshots is instant and cheap.
// 1. Commit current state
await db.commit();
// 2. Create a named snapshot
await db.createSnapshot("backup_v1");
// 3. Make destructive changes
await users.delete(1);
await db.commit();
// 4. Time Travel: Access the old data
const snapshot = await db.getSnapshot("backup_v1");
const oldUsers = snapshot.getSet("users", "doc");
console.log(await oldUsers.get(1)); // The user still exists here!You can mount a btrdb database as a real folder on Linux/macOS using FUSE. See btrdbfs/README.md for details.
# Install CLI
npm install -g @yuuza/btrdbfs
# Mount database
btrdbfs my_data.db ./mountpointbtrdb includes a basic HTTP server for remote access.
import { HttpApiServer } from "@yuuza/btrdb";
// ... open db ...
new HttpApiServer(db).serve(Deno.listen({ port: 1234 }));btrdb uses a B-tree Copy-on-Write structure.
- Pages: Data is stored in fixed-size pages (default 4KB).
- Immutability: When a page is modified, it is not overwritten. Instead, it is copied to a new location, modified there, and the parent node is updated to point to the new address.
- Root Tree: This bubble-up effect reaches the "SuperPage" (Root). By keeping a reference to an old Root address, you effectively have a snapshot of the entire database at that moment.
MIT License.