Kysely — TypeScript SQL 查询构建器
已复核Kysely 是一个 TypeScript 优先的 SQL 查询构建器。日常类比:它不替你炒菜,只给你一套带量杯的锅铲——你还是在写 select / where / join,但列名和值类型会在编译期被 Database 接口核对。
const user = await db .selectFrom("users") .select(["id", "email"]) .where("id", "=", 1) .executeTakeFirst();execute() 返回全部 rows;executeTakeFirst() 只取第一行;executeTakeFirstOrThrow() 空结果时默认抛 NoResultError。0.29.5 运行时 0 dependencies,声明 Node >=22。
不理解 Kysely 把“类型”和“执行”拆开,就解释不了:
- 为什么没有
schema.prisma也能得到{ id: number; email: string } | undefined - 为什么
compile()可以只出 SQL、不碰数据库 - 为什么内置
Migrator仍然不会根据 interface 自动 diff 出 ALTER - 为什么 TypeScript 4.x 项目装上 0.29.5 只会看到过期 stub
固定 0.29.5 的主链可以拆成五步:
-
手写
Database接口:表名是 key,列用string/Generated<T>/ColumnType<S, I, U>描述 select / insert / update 三种形状。 -
dialect 装配执行器:构造
Kysely时,dialect.createDriver()、createQueryCompiler()、createAdapter()组成DefaultQueryExecutor。一等 dialect 有 postgres、mysql、sqlite、mssql、pglite。 -
不可变 builder:
selectFrom/insertInto/updateTable/deleteFrom/mergeInto每一步返回新对象,类型沿链累积。 -
编译:
toOperationNode()先跑 plugin 的transformQuery(必须保持 node kind),再compileQuery得到{ sql, parameters }。 -
执行:
executeQuery向 driver 要连接、跑 SQL,再跑transformResult。AbortSignal可选;默认inflightQueryAbortStrategy是ignore query。
案例 1:Database 类型 + dialect
Section titled “案例 1:Database 类型 + dialect”import { Kysely, Generated, ColumnType, PostgresDialect } from "kysely";import { Pool } from "pg";
interface Database { users: { id: Generated<number>; email: string; name: string | null; created_at: ColumnType<Date, string | undefined, never>; };}
const db = new Kysely<Database>({ dialect: new PostgresDialect({ pool: new Pool({ connectionString: process.env.DATABASE_URL }), }),});Generated<S> 等于 ColumnType<S, S | undefined, S>:insert 可省略,update 仍是 S。pool 也可以是 async () => new Pool(...),第一次用到才创建。
案例 2:compile 与 execute 分开
Section titled “案例 2:compile 与 execute 分开”const qb = db .selectFrom("users") .select(["id", "email"]) .where("id", "=", 1);
const compiled = qb.compile();// compiled.sql / compiled.parameters 已是 dialect SQL
const user = await qb.executeTakeFirst();类型安全停在编译期。同事改了真实列类型但没改 interface,TS 不会报警。
案例 3:一等 Migrator,不是 schema diff
Section titled “案例 3:一等 Migrator,不是 schema diff”import { FileMigrationProvider, Migrator } from "kysely/migration";import { promises as fs } from "node:fs";import path from "node:path";
const migrator = new Migrator({ db, provider: new FileMigrationProvider({ fs, path, migrationFolder: "migrations", }),});
await migrator.migrateToLatest();默认记录表是 kysely_migration,锁表是 kysely_migration_lock。up / down 是你自己写的 Kysely 语句;它不会读取 Database 接口去生成 SQL。
-
把“没有 Prisma migrate”说成“没有 migration”:0.29.5 在
kysely/migration提供Migrator与FileMigrationProvider。缺的是自动 schema diff,不是迁移运行器。 -
忽略 Node / TypeScript 下限:
engines.node是>=22.0.0;<5.4的 TypeScript 只会解析到outdated-typescript.d.ts。 -
信任手写 interface 等于库状态:Kysely 不 generate 客户端。interface 过期时,编译期绿灯、运行时列类型仍可能对不上。
-
plugin 改 query 种类:
transformQuery必须返回同一kind,否则执行器直接抛错。 -
把
v0.30.0-beta当 0.29.5 合同:beta tag 已存在;本页只绑定v0.29.5。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 已有 SQL 库,想要类型安全但不想引入 codegen 客户端
- 需要
compile()先审查 SQL,再决定是否执行 - Node 22+,并接受自己维护
Database接口或外部 codegen
不适用:
- 想从一份 DSL 同时得到 migrate SQL 和生成客户端 → 看 prisma
- TypeScript 低于 5.4,或必须跑在 Node 20
- MongoDB:0.29.5 的 dialect 都是 SQL
- 需要本页未测量的“30KB / 更快冷启动”结论
固定版本边界
Section titled “固定版本边界”- 本文绑定
kysely-org/kysely@f24018c7...,tag 与 package 均为0.29.5。 - 运行时 0 dependencies;官方 helpers、
readonly与 plugin 是可选出口。 - 一等 dialect:PostgreSQL、MySQL、SQLite、MS SQL Server、PGlite。社区 adapter 不在本提交保证范围内。
- 本文未安装
pg/better-sqlite3、未跑上游 mocha / tsd、未测 bundle,状态保持UNVERIFIED。
- builder 可以 1:1 贴近 SQL——代价是关系嵌套、schema 演化都要自己接。
- 类型安全不是运行时校验——
Database接口是编译期契约,不是 introspection 结果。 - 迁移运行器 ≠ schema 源头——
Migrator执行你写的up/down,不替你算 diff。 - dialect 是四件套——driver、compiler、adapter、introspector 可以替换,查询 API 保持同一套。
executeTakeFirst()在 0 行结果时返回什么?和executeTakeFirstOrThrow()差在哪?- 不提供
down()的 migration,往下回滚时会怎样? - plugin 把
SelectQueryNode变成另一种kind,执行前会怎样?
检查点:
- 返回
undefined;OrThrow 默认抛NoResultError。 - 这条 migration 在 down 方向被跳过。
transformQuery抛错,因为 kind 必须保持不变。
- 文档:kysely.dev/docs/intro
- 固定源码:kysely-org/kysely —— 本文绑定提交
f24018c789c3cf7ad03ccc672ada63a1ded87f88 - prisma —— schema-first ORM 对照
- prisma —— schema + codegen 对照;Kysely 把 schema 真相留在 TypeScript 接口
- typescript —— 0.29.5 要求 TS 5.4+ 才能看到真实类型
- postgresql ——
PostgresDialect使用pgPool