← 返回信息流
AI 资讯Hacker News·1 小时前

Show HN:Bun-sqlgen 为 Bun 提供类型安全的原生 SQL 支持

原标题:Show HN: Bun-sqlgen – Type-safe raw SQL for Bun, no ORM

速览

Bun-sqlgen 是一个开源工具,旨在为 Bun 运行时环境提供类型安全的原生 SQL 查询生成能力。它允许开发者在不使用 ORM 的情况下,依然享受编译时的类型检查与自动补全优势。该工具简化了数据库交互流程,提升了代码的安全性与开发效率。

AI 深度解读

Show HN: Bun-sqlgen – 为 Bun 提供类型安全的原生 SQL,无需 ORM

背景

在 JavaScript/TypeScript 生态中,数据库交互长期存在一个痛点:要么使用 ORM(对象关系映射)库,虽然提供了类型安全,但往往带来性能开销、复杂的配置以及“魔法”行为;要么直接使用原生 SQL 字符串,虽然灵活且性能极佳,但缺乏类型检查,容易导致拼写错误、列名不匹配或空值处理不当等运行时错误。

Bun 作为一个主打高性能的 JavaScript 运行时,其内置的 bun:sqlitebun:pg 提供了极快的原生数据库支持。然而,原生 API 本身并不提供 TypeScript 类型推导。bun-sqlgen 的出现正是为了解决这一矛盾:它旨在为 Bun 用户提供一种无需 ORM、无需手写类型定义、也无需泛型(generics)的解决方案,通过代码生成技术,让原生 SQL 查询具备完整的类型安全和空值检查能力。

核心内容

bun-sqlgen 是一个发布在 npm 上的工具包(包名为 @ilbertt/bun-sqlgen),它通过代码生成(Codegen)的方式,将数据库模式(Schema)与 TypeScript 类型系统紧密绑定。其核心工作流程和特性如下:

1. 基于真实数据库的模式检查

与许多仅解析 SQL 字符串的工具不同,bun-sqlgen 会在构建阶段连接真实的 PostgreSQL 或 SQLite 数据库(无需 Docker 容器),直接读取数据库模式。这意味着它生成的类型完全基于数据库的实际结构。如果 SQL 查询中存在错误的列名或无效的 SQL 语法,构建过程将直接失败,从而将错误拦截在开发阶段,而非生产环境。由于速度极快,该过程甚至可以配置为在每次文件保存时自动运行。

2. 标签模板与类型推导

开发者可以使用 withTypes 包装 Bun 的 SQL 客户端,并为每个查询打上名称标签(Tag)。例如:

import { withTypes } from '@ilbertt/bun-sqlgen';
import { SQL } from 'bun';

// 包装客户端,启用类型推导
const sql = withTypes(new SQL(Bun.env.DATABASE_URL!));

export async function getUser(id: number) {
  // 使用标签 GetUser 标记查询
  const [user] = await sql.GetUser`
    SELECT id, email, display_name FROM users WHERE id = ${id}
  `;
  // user 的类型被自动推导为: { id: string; email: string; display_name: string | null }
  return user;
}

这种写法不仅保留了原生 SQL 的灵活性,还让 IDE 能够实时提供智能提示和类型检查。返回的行对象是“空值安全”(null-safe)的,TypeScript 会明确标记可能为 null 的字段。

3. 代码生成与构建时验证

工具通过运行 bun bun-sqlgen generate 命令,扫描项目中的 TypeScript 文件及迁移文件(Migrations),生成类型定义文件 src/queries.gen.d.ts。开发者应将此文件提交至版本控制。

一旦生成文件就位,TypeScript 编译器将强制执行类型检查:

  • 访问不存在的属性(如 user.emial)会导致编译错误。
  • 对可能为 null 的属性进行不安全操作(如 user.display_name.length)会被标记为潜在错误。

这一切均由纯 TypeScript 实现,无需额外的运行时依赖。

4. 迁移文件作为单一事实来源

bun-sqlgen 将数据库迁移文件(Migrations)视为模式的唯一事实来源(Source of Truth)。迁移文件可以放置在任意文件夹中(如 db/migrations/),工具会解析这些 SQL 文件来构建类型映射。

示例迁移文件:

-- db/migrations/0001_init.sql
CREATE TABLE users (
  id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  email text NOT NULL,
  display_name text
);

5. 配置与文档

该工具支持多种方言(PostgreSQL 和 SQLite)、空值覆盖(nullability overrides)、事务处理以及详细的配置选项。完整的指南包含在项目的 README 中,示例项目位于 examples/ 文件夹,开发规范和贡献指南则在 CONTRIBUTING.md 中。

关键要点

  • 零 ORM 依赖:完全摒弃了 Prisma、Drizzle 等 ORM 库,保持 100% 的 Bun 原生性能。
  • 构建时安全:错误的列名、无效的 SQL 语法会在构建阶段报错,而非在运行时崩溃。
  • 无需 Docker:直接连接本地或远程的真实数据库进行模式检查,简化了开发环境配置。
  • 自动类型推导:通过标签模板(Tagged Templates)和代码生成,自动推断查询结果的 TypeScript 类型,包括空值处理。
  • 极速迭代:生成过程足够快,支持在每次文件保存时重新运行,提升开发体验。
  • 迁移驱动:以 SQL 迁移文件为模式源头,确保类型定义与数据库结构实时同步。
  • 纯 TypeScript 实现:生成的类型定义文件由 plain-ts 处理,无额外运行时开销。

意义与影响

bun-sqlgen 代表了现代全栈开发中对“类型安全”与“性能”双重追求的进一步细化。对于 Bun 用户而言,它填补了原生 SQL 缺乏类型安全这一关键空白。

  1. 提升开发信心与代码质量:通过将数据库模式的变更即时反映到 TypeScript 类型系统中,开发者可以自信地重构数据库结构,而无需担心破坏现有的查询逻辑。
  2. 简化技术栈:对于追求极致性能和简单性的项目,开发者不再需要在“ORM 的便利性”和“原生 SQL 的性能”之间做出妥协。bun-sqlgen 提供了两者的最佳结合点。
  3. 推动 Bun 生态成熟:随着更多类似工具的出现,Bun 的数据库交互体验将变得更加完善,吸引更多企业级应用采用 Bun 作为后端运行时。
  4. 标准化 SQL 类型安全实践:其“迁移文件作为单一事实来源”的理念,有助于在团队中建立更规范的数据库版本控制和类型管理流程。

总之,bun-sqlgen 为 Bun 社区提供了一个轻量、高效且类型安全的 SQL 查询解决方案,特别适合那些重视性能、希望避免 ORM 复杂性,但又需要严格类型检查的 TypeScript 项目。

查看原文 →github.com