Fast, durable, embedded key-value storage for Node.js and Electron.
Get Level-style APIs, ACID transactions, sorted keys, and LSM-tree persistence without running an external database process.
- Embedded and simple: no server process, no setup, no runtime dependencies.
- Durable by design: append-only log + LSM compaction + crash recovery.
- ACID transactions:
startTx/endTx/abortTx, plus atomicbatch. - Sorted keys for efficient range/offset queries.
- Worker-thread mode by default to avoid blocking your main app thread.
- Supports both ESM and CommonJS.
- Supports
Buffervalues andBufferkeys.
npm i snap-db// ESM
import { SnapDB } from "snap-db";// CommonJS
const { SnapDB } = require("snap-db");import { SnapDB } from "snap-db";
const db = new SnapDB({
dir: "./my-db",
key: "string"
});
await db.ready();
await db.put("user:1", "Alice");
console.log(await db.get("user:1")); // "Alice"
await db.close();SnapDB supports Buffer data and keys.
const db = new SnapDB({ dir: "./bin-db", key: "string" });
await db.ready();
await db.put("blob:1", Buffer.from([0, 1, 2, 3, 254, 255]));
const value = await db.get("blob:1");
console.log(Buffer.isBuffer(value)); // trueUse either key: "buffer" or key: "any".
const db = new SnapDB({ dir: "./buf-keys", key: "buffer" });
await db.ready();
const key = Buffer.from([0x00, 0x10, 0x20]);
await db.put(key, "payload");
console.log(await db.get(Buffer.from([0x00, 0x10, 0x20]))); // "payload"new SnapDB({
dir: string,
key?: "string" | "int" | "float" | "any" | "buffer",
cache?: boolean,
autoFlush?: boolean | number,
mainThread?: boolean
})
// shorthand:
new SnapDB(dir: string, keyType?: "string" | "int" | "float" | "any" | "buffer", cache?: boolean)| Option | Type | Default | Notes |
|---|---|---|---|
dir |
string |
required | Directory used for persistence. |
key |
"string" | "int" | "float" | "any" | "buffer" |
"any" |
Key mode for parsing/sorting/serialization. |
cache |
boolean |
false |
In-memory value cache (faster reads, higher RAM). |
autoFlush |
boolean | number |
true |
false disables auto flush; number = MB threshold. |
mainThread |
boolean |
false |
If true, DB operations run in-process (faster but blocking). |
| Mode | Accepted keys |
|---|---|
string |
cast to string |
int |
cast with parseInt |
float |
cast with parseFloat |
buffer |
cast to Buffer |
any |
number, string, Buffer (other values stringified) |
When using any, key sort precedence is: number -> string -> Buffer.
All APIs support Promise style; many also support callbacks.
ready(callback?) => Promise<void>isOpen() => booleanisClosed() => booleanclose(callback?) => Promise<void>empty(callback?) => Promise<void>flushLog(callback?) => Promise<void>
put(key, value: string | Buffer, callback?) => Promise<any>get(key, callback?) => Promise<string | Buffer | undefined>delete(key, callback?) => Promise<any>del(key, callback?) => Promise<any>(alias)exists(key, callback?) => Promise<boolean>getCount(callback?) => Promise<number>
startTx(callback?) => Promise<number>endTx(callback?) => Promise<number>abortTx(callback?) => Promise<number>begin_transaction() => Promise<any>(legacy alias)end_transaction() => Promise<any>(legacy alias)
batch(ops, callback?) => Promise<void>batch().put(...).del(...).write(callback?)
batch(ops) executes atomically by wrapping operations in a transaction.
query(args, onRecord, onComplete)getAll(onRecord, onComplete, reverse?)getAllKeys(onRecord, onComplete, reverse?)range(lower, higher, onRecord, onComplete, reverse?)offset(offset, limit, onRecord, onComplete, reverse?)
queryIt(args)getAllIt(reverse?)getAllKeysIt(reverse?)rangeIt(lower, higher, reverse?)offsetIt(offset, limit, reverse?)
queryStream(args)getAllStream(reverse?)getAllKeysStream(reverse?)rangeStream(lower, higher, reverse?)offsetStream(offset, limit, reverse?)createReadStream(args)(Level-style)createKeyStream(args)(Level-style)createValueStream(args)(Level-style)
type QueryArgs<K> = {
gt?: K;
gte?: K;
lt?: K;
lte?: K;
offset?: number;
limit?: number;
keys?: boolean;
values?: boolean;
reverse?: boolean;
}Notes:
offsetmode should not be mixed withgt/gte/lt/lte.range(lower, higher)uses lower-inclusive / upper-exclusive behavior in forward mode.values: falseavoids loading values and can be significantly faster.
db.on("compact-start", (ev) => {});
db.on("compact-end", (ev) => {});
db.off("compact-start", handler);Supported event names:
readygetputdeleteget-keysget-keys-endget-countget-queryget-query-endget-allget-all-endget-offsetget-offset-endget-rangeget-range-endread-streamread-stream-endread-key-streamread-key-stream-endread-value-streamread-value-stream-endtx-starttx-endtx-abortcloseclearcompact-startcompact-end
SnapDB provides Level-style APIs (batch, streams, range/query patterns) while also adding transaction controls (startTx/endTx/abortTx), worker-mode operation, and manual compaction control (flushLog).
- For highest throughput and least app blocking, keep
mainThread: false(default). - For lower latency and no worker serialization overhead, use
mainThread: truein controlled environments. - If you disable
autoFlush, callflushLog()periodically. cache: truecan improve read latency for hot datasets but increases memory usage.
npm run build
npm test