Overview

A columnar SQL engine that runs entirely in the browser.

Minnow is a SQL database that runs in the browser. You give it SQL; it parses, plans, optimizes, and executes that SQL against columnar data stored in IndexedDB. There is no server, no WebAssembly module to download and compile, and no build step.

import { MinnowDatabase } from "@minnowdb/core";
import { IndexedDbBlockStore } from "@minnowdb/core/storage";

const db = new MinnowDatabase(await IndexedDbBlockStore.open({ name: "shop" }));

await db.execute(`CREATE TABLE orders (
  order_id INTEGER PRIMARY KEY,
  customer_id INTEGER NOT NULL,
  total DOUBLE PRECISION NOT NULL,
  placed_at TIMESTAMP NOT NULL
)`);

const { rows } = await db.query(`
  SELECT DATE_TRUNC('month', placed_at) AS month, ROUND(SUM(total), 2) AS revenue
  FROM orders
  GROUP BY DATE_TRUNC('month', placed_at)
  ORDER BY month DESC
`);

Experimental

The version-zero block format carries no compatibility promise, and the SQL surface is a correctness-first subset of SQL:2023 rather than the whole standard. Everything the engine does and deliberately refuses to do is listed, keyed to the standard's own feature identifiers, in the feature matrix.

What is here

The engine is the product. Parser, planner, optimizer, and a vectorized executor are implemented in this repository — there is no SQLite or DuckDB underneath, and no WebAssembly anywhere.

  • SQL — how to run statements, and what the language surface is: joins, CTEs, window functions, set operations, grouping sets, upserts with RETURNING, full-text search.
  • Schema — declaring tables, constraints, and views in TypeScript, and what a migration will and will not do to a database that already holds rows.
  • Typed client — the optional query builder: inferred row types, typed writes, and live queries.
  • Engine — the MinnowDatabase API, transactions and snapshots, running the engine in a worker, and the memory budget.
  • Storage — the block store contract and the two adapters that implement it, plus snapshots and background maintenance.

What it is for

Minnow suits an application that already has data on the device and wants to ask real questions of it: an offline-first tool, a local analytics view, a large table a user needs to slice without a round trip. It is a database, not a cache — writes are durable, commits are atomic across tabs, and a reader never sees half of a write.

It is not a replacement for a server database. Everything runs on one machine, against one browser's storage quota, with one writer's worth of throughput.

Design commitments

Four rules are fixed. The reasoning is in Architecture.

  • IndexedDB is the source of truth. Correctness never depends on BroadcastChannel, Web Locks, or page-close handlers. Durability ends at a committed IndexedDB transaction.
  • Published data is immutable. Writes append and publish atomically; another tab sees the old version or the new one, never a partial write.
  • Reads are snapshot reads. A query executes against one version. Reads never block writes and writes never block reads.
  • The engine is our own. No embedded database underneath, and no Wasm to fetch before the first query answers.

On this page