Kysely + better-sqlite3 typed queries: working versions (tested 2026-10-15)
Type-safe SQL with Kysely on SQLite, including a schema migration, joins and a transaction.
passed1 passed · 0 failed2026-10-15 11:36 UTC- Status
- passed
- Last passed
- 2026-10-15 11:36 UTC
- Tests
- 1 / 1
- Language
- TypeScript
- Runtime
- node v22.23.3
npm install --no-audit --no-fund --ignore-scripts=false npm test
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38728226240
The code
Exactly the files that passed. Your agent can pull them with get_recipe("kysely-sqlite-queries") (install), or see them on GitHub.
{
"name": "kysely-sqlite-queries",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"kysely": "0.29.6",
"better-sqlite3": "12.11.1"
},
"devDependencies": {
"tsx": "4.23.15",
"@types/node": "24.19.2",
"@types/better-sqlite3": "9.6.0"
}
}
import Database from "better-sqlite3";
import { Kysely, SqliteDialect, type Generated } from "kysely";
interface DB {
author: { id: Generated<number>; name: string };
book: { id: Generated<number>; author_id: number; title: string };
}
export async function createDb() {
const db = new Kysely<DB>({ dialect: new SqliteDialect({ database: new Database(":memory:") }) });
await db.schema.createTable("author").addColumn("id", "integer", (c) => c.primaryKey().autoIncrement()).addColumn("name", "text", (c) => c.notNull()).execute();
await db.schema.createTable("book").addColumn("id", "integer", (c) => c.primaryKey().autoIncrement())
.addColumn("author_id", "integer", (c) => c.notNull().references("author.id")).addColumn("title", "text", (c) => c.notNull()).execute();
return db;
}
export async function addAuthorWithBooks(db: Kysely<DB>, name: string, titles: string[]) {
return db.transaction().execute(async (trx) => {
const { id } = await trx.insertInto("author").values({ name }).returning("id").executeTakeFirstOrThrow();
for (const title of titles) await trx.insertInto("book").values({ author_id: id, title }).execute();
return id;
});
}
export const booksByAuthor = (db: Kysely<DB>, name: string) =>
db.selectFrom("book").innerJoin("author", "author.id", "book.author_id").select(["book.title"]).where("author.name", "=", name).orderBy("book.title").execute();
import { test } from "node:test";
import assert from "node:assert/strict";
import { createDb, addAuthorWithBooks, booksByAuthor } from "../src/db.ts";
test("transaction insert and join query", async () => {
const db = await createDb();
await addAuthorWithBooks(db, "Le Guin", ["The Dispossessed", "A Wizard of Earthsea"]);
const rows = await booksByAuthor(db, "Le Guin");
assert.deepEqual(rows.map((r) => r.title), ["A Wizard of Earthsea", "The Dispossessed"]);
await db.destroy();
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
Drizzle ORM + better-sqlite3 CRUD
Typed schema, migrations-free table creation and CRUD with Drizzle on an in-memory SQLite database.
Prisma 7 + SQLite via better-sqlite3 adapter
Prisma Client 7 with the better-sqlite3 driver adapter, schema push and relational queries on a local SQLite file.
Prisma 7 + SQLite: nested writes, interactive transactions and unique errors
Prisma Client 7 on SQLite (better-sqlite3 adapter): nested creates, an interactive $transaction that rolls back, and handling P2002 unique-constraint errors.
Drizzle ORM + drizzle-zod validated inserts on SQLite
Generate Zod insert and select schemas from Drizzle tables with drizzle-zod, refine them, and validate input before inserting into SQLite.
All Kysely recipesAll better-sqlite3 recipesAll SQLite recipes