← 中文资源

简体中文版本

Kysely 类型安全查询构建器

用 Kysely 构建全类型化的 SQL:表结构以接口声明、动态条件按需拼接、select 列表精确决定结果行的字段类型。

阅读英文原文

示例说明

Kysely 是无实体类的查询构建器——数据库形态用接口描述而非装饰器类,SQL 的每个片段都带类型。动态筛选是最见功力的场景:条件按需链式追加,query 变量的类型随之演进,拼错列名或比较了不兼容的值都过不了编译;select 的列别名直接成为结果对象的键类型。

技术要点

  • 接口声明表结构避免实体类负担
  • 动态 where 保持类型连贯性
  • select 别名决定结果行形状

代码解读

searchArticles 以可选字段表达筛选条件:作者名存在时追加等值过滤,仅发布标志为真时追加布尔条件。select 同时取文章列与作者名别名,rows 的元素类型即 id、title、author 三字段对象。Generated 类型标记自增列,插入时省略。

使用建议

原生片段用 sql 模板包裹保持参数化。迁移用 Kysely 自带迁移器管理。连接池大小与语句超时按生产负载配置。

代码示例

import { Kysely, PostgresDialect, Generated, sql } from 'kysely';
import { Pool } from 'pg';

interface Database {
  articles: {
    id: Generated<number>;
    title: string;
    authorId: number;
    published: boolean;
  };
  authors: {
    id: Generated<number>;
    name: string;
  };
}

const db = new Kysely<Database>({
  dialect: new PostgresDialect({
    pool: new Pool({ connectionString: process.env.DATABASE_URL }),
  }),
});

type Filter = { author?: string; onlyPublished?: boolean };

async function searchArticles(filter: Filter) {
  let query = db
    .selectFrom('articles')
    .innerJoin('authors', 'authors.id', 'articles.authorId')
    .select(['articles.id', 'articles.title', 'authors.name as author']);

  if (filter.author !== undefined) {
    query = query.where('authors.name', '=', filter.author);
  }
  if (filter.onlyPublished) {
    query = query.where('articles.published', '=', true);
  }

  const rows = await query
    .orderBy('articles.id', 'desc')
    .limit(25)
    .execute();

  return rows;
}

const fresh = await searchArticles({ onlyPublished: true });
console.log('typed author column:', fresh[0]?.author.length ?? 0);
await db.destroy();