Choose the runtime boundary first
| Boundary | Strength | Cost |
|---|---|---|
| Native Node addon | In-process calls, direct buffers, low latency | Platform binaries, ABI/toolchain packaging, blocking-I/O discipline |
| WASM | Portable sandbox and simpler distribution in supported environments | Filesystem, threading, memory, and native-library limits |
| Pure JavaScript | Easy installation and debugging | CPU-heavy indexing and persistence compete on the JS runtime |
| Local daemon | Many processes/languages share one warm database | Network protocol, lifecycle, auth, and service management |
Do not select a database from benchmark charts before deciding which boundary your product can ship and operate.
Map the workload to an engine family
- Relational application state: SQLite remains the default benchmark because of maturity, portability, tooling, and a huge ecosystem.
- Local analytical scans: DuckDB is optimized for analytical SQL and columnar execution.
- Ordered key-value access: LevelDB/RocksDB-style engines provide a smaller data model with predictable sorted-key behavior.
- Reactive browser/local-first data: synchronization and browser support may matter more than server-side SQL.
- Operational plus AI retrieval: evaluate transaction semantics, vector indexes, lexical search, filters, and reranking together.
MongrelDB's Node.js boundary
MongrelDB ships a native NAPI addon for Node.js 22 or newer. The embedded Database API owns a local root in-process. RemoteDatabase connects to mongreldb-server when multiple processes need one owner. Published prebuilt addon binaries currently cover Linux x64 and arm64; other platforms build from source or use the daemon client.
const { Database, ColumnType } = require("@visorcraft/mongreldb");
const db = Database.withPath("./app-data");
db.createTable("events", {
columns: [
{ id: 1, name: "id", ty: ColumnType.Int64,
primaryKey: true, nullable: false },
{ id: 2, name: "kind", ty: ColumnType.Bytes,
primaryKey: false, nullable: false }
],
indexes: []
});
const events = db.table("events");
events.put([
{ columnId: 1, int64: 1n },
{ columnId: 2, text: "startup" }
]);
events.commit();Writes require an explicit commit for durability. Async variants move blocking work to the NAPI blocking pool. SQL runs through DataFusion and returns Arrow IPC bytes, which Node can decode with Apache Arrow.
Questions that expose hidden costs
- Does installation succeed on every OS and CPU you ship?
- Can the engine recover after process termination during commit?
- Does it enforce one owner per storage root, and how do worker processes share access?
- Are 64-bit identifiers returned without JavaScript number truncation?
- Can large result sets move as batches rather than per-cell callbacks?
- How are backups coordinated with open handles?
- Can queries be cancelled and bounded?
- What migration and schema tooling exists?
MongrelDB returns RowIds, counts, and epochs as BigInt, and Arrow IPC avoids per-cell object construction for analytical result paths.
Run a vertical evaluation
Build one narrow production-shaped path: create schema, write a transaction, restart, query by your most important predicate, scan a representative projection, take a backup, and restore it. Add vector search only if the product needs it. Measure package size and installation failure rate alongside latency.
MongrelDB Viewer
Open the same database outside your Node application to inspect schema, rows, indexes, SQL, and maintenance.
Mongrel by VisorCraft
Use Mongrel when the Node application also touches PostgreSQL, MongoDB, Redis, containers, Kubernetes, remote shells, or HTTP APIs.