# Kysely

> Run Kysely queries against Minnow in the main thread or a worker.

`@minnowdb/kysely` lets an existing Kysely application use Minnow as its browser-local database.
It uses Kysely's PostgreSQL compiler, so generated statements use double-quoted identifiers, `$1`
parameters, `RETURNING`, and the PostgreSQL forms Minnow supports.

```bash
npm install @minnowdb/core @minnowdb/kysely kysely
```

The adapter supports Kysely 0.29.x.

## Define once and connect

Pass the same schema you migrate to `createKysely`. It derives Kysely's complete `DB` map, so
tables and columns are not retyped. `migrate()` changes the database; `createKysely()` only creates
the query interface, so keep the explicit migration call:

```ts
import { createKysely } from "@minnowdb/kysely";
import { MinnowDatabase, column, schema, table } from "@minnowdb/core";
import { IndexedDbBlockStore } from "@minnowdb/core/storage";

const orders = table("orders", {
  order_id: column.integer().unique().autoIncrement(),
  customer: column.string(),
  total: column.numeric({ precision: 12, scale: 2 }).default("0"),
  status: column.enum(["open", "closed"]).default("open"),
  note: column.string().nullable(),
});
const appSchema = schema([orders]);

const database = new MinnowDatabase(await IndexedDbBlockStore.open({ name: "shop" }));
await database.migrate(appSchema);
const db = createKysely({ driver: database, schema: appSchema });

const orderRows = await db
  .selectFrom("orders")
  .select(["order_id", "total"])
  .where("total", ">=", 25)
  .orderBy("total", "desc")
  .execute();
```

The inferred Kysely columns retain Minnow's separate select, insert, and update types:

- defaults and nullable columns are optional on insert;
- enum values remain literal unions;
- exact `NUMERIC` selects as a lossless `string`, while writes and predicates naturally accept
  `string | number`;
- scalar and composite primary-key columns cannot be updated;
- declared views select normally and accept no column values on insert or update.

If an existing factory constructs Kysely, use the exported type directly:

```ts
import { Kysely } from "kysely";
import { MinnowDialect, type InferKyselyDatabase } from "@minnowdb/kysely";

type DB = InferKyselyDatabase<typeof appSchema>;
const db = new Kysely<DB>({
  dialect: new MinnowDialect({ driver: database, schema: appSchema }),
});
```

Pass `schema` to the dialect when you construct Kysely yourself. It supplies the exact DB type and
lets the compiler normalize a batch of empty value objects. Literal and SQL-expression defaults
live only in Minnow's catalog, so Kysely, raw SQL, batches, workers, and other tabs all get the same
behavior. There is no adapter-side generation.

An empty Kysely value object compiles to PostgreSQL's normal `DEFAULT VALUES` form. A batch of
empty objects uses `DEFAULT` value slots, so catalog defaults run once per row without
adapter-specific SQL.

The generated SQL stays visible and ordinary. Omitted object properties compile to SQL omission
or `DEFAULT`; explicit `null` remains a bound NULL:

```ts
const query = db.insertInto("orders").values({ customer: "Ada" });
const compiled = query.compile();
// compiled.sql:
// insert into "orders" ("customer") values ($1)
// compiled.parameters: ["Ada"]
```

The catalog fills `order_id`, `total`, and `status` when the statement executes; Kysely does not
hide generated parameters in the compiled query. `INSERT ... SELECT` may omit default-bearing
target columns for the same reason: the engine evaluates their stored SQL once per selected row.

`MinnowDatabaseClient` works in the same position, so moving the engine to a worker changes only
how `database` is constructed. Both implement `MinnowSqlDriver` structurally.

## Supported Kysely operations

- Reads, inserts, updates, deletes, and their `$n` parameters.
- `RETURNING` rows and Kysely's affected-row counts.
- `db.transaction().execute(...)` through Minnow's `BEGIN`, `COMMIT`, and `ROLLBACK`.
- Raw SQL savepoints inside that transaction.
- Kysely schema statements that stay inside Minnow's supported DDL profile.
- `db.introspection`: tables, views, columns, nullability, defaults, auto-increment, and logical
  types. Exact `NUMERIC(p, s)`, JSON/JSONB, UUID, TIME, INTERVAL, arrays, and enum names are
  reported instead of being flattened to `text`.

Minnow has one logical connection. Kysely serializes work through it and a transaction sees its
own staged writes. `db.destroy()` releases Kysely but does not close the caller-owned Minnow
database or worker client; close that driver separately when the application is done.

## Limits

Minnow uses PostgreSQL-style SQL, but it is an embedded database rather than a PostgreSQL server:

- `getSchemas()` returns an empty list. There are no catalogs or schemas to invent.
- Transaction access modes and isolation levels are rejected; Minnow has one snapshot model.
- The engine accepts `SAVEPOINT`, `ROLLBACK TO`, and `RELEASE`; Kysely's dialect interface
  exposes one top-level transaction rather than a nested transaction helper.
- Streaming is rejected because Minnow returns a completed result set.
- Parameters must be `boolean`, `number`, `string`, `Date`, or `null`; BigInt, byte arrays, and
  driver-native PostgreSQL objects have no Minnow parameter representation. Exact decimals accept
  numbers or strings as inputs and return lossless strings. JSON/JSONB, UUID, arrays, `TIME`,
  intervals, and enums cross this adapter as strings.
- Kysely can generate PostgreSQL features outside Minnow's profile. Check the executable
  [feature matrix](/docs/sql/feature-matrix.md); the engine rejects unsupported SQL explicitly.

Kysely reserves the connection during a migration, so one `Kysely` instance cannot race itself.
That does not coordinate migrations across tabs. For shared application startup, use Minnow's
schema declaration and `database.migrate(schema)`, which coordinates through Minnow's catalog.

## Compatibility checks

Adapter tests compile and execute DDL, inserts, queries, `RETURNING`, commits, rollbacks, and
introspection. Minnow's broader [SQL compatibility suite](/docs/conformance.md) compares supported
PostgreSQL reads and writes with PGlite and checks every documented difference and exclusion.

---

Minnow 0.3.0 · this page on the site: /docs/adapters/kysely/
