Skip to main content
You write queries as tagged template literals, and the client supports connection pooling, transactions, and prepared statements.
db.ts

功能特性

  • 带标签的模板字面量,可防止 SQL 注入
  • 事务
  • 命名参数和位置参数
  • 连接池
  • BigInt 支持
  • SASL(SCRAM-SHA-256)、MD5 和明文身份验证
  • 连接超时
  • 将行作为数据对象、数组的数组或 Buffer 返回
  • 二进制协议支持,可提升速度
  • TLS 支持(以及身份验证模式)
  • 使用环境变量自动配置
  • PostgreSQL LISTEN / NOTIFY

数据库支持

Bun.SQL 提供多数据库系统的统一 API:

PostgreSQL

Bun uses PostgreSQL when:
  • 连接字符串不符合 SQLite 或 MySQL 格式(作为默认适配器)
  • 明确使用 postgres:// 或 postgresql:// 协议的连接字符串
  • 无连接字符串,但环境变量指向 PostgreSQL
db.ts

MySQL

MySQL 内置于 Bun.SQL 中,使用相同的标签模板字面量接口,并兼容 MySQL 5.7+ 和 MySQL 8.0+:
db.ts
MySQL 支持多种 URL 格式作为连接字符串:
MySQL 数据库支持:
  • 预处理语句:参数化查询自动创建且缓存语句
  • 二进制协议:提升预处理语句性能并支持准确类型处理
  • 多结果集:支持存储过程返回多个结果集
  • 认证插件:支持 mysql_native_password、caching_sha2_password(MySQL 8.0 默认)和 sha256_password
  • SSL/TLS 连接:SSL 模式配置类似 PostgreSQL
  • 连接属性:客户端信息发送到服务器便于监控
  • 查询流水线:无需等待响应即可执行多个预处理语句
MySQL 8 账户默认使用 caching_sha2_password 插件。用户的首次连接需要完整身份验证:在未加密的连接上,客户端必须下载服务器的 RSA 公钥来加密密码。Bun 默认拒绝这样做(与 mysql2 和 Connector/J 的默认设置相同),因为网络路径上的攻击者可能替换自己的密钥并获取密码。连接会失败,并显示 ERR_MYSQL_PUBLIC_KEY_RETRIEVAL_NOT_ALLOWED。请使用 TLS 连接;如果你信任网络环境(例如本地开发数据库),也可以显式选择启用:

SQLite

SQLite 内置于 Bun.SQL 中,使用相同的标签模板字面量接口:
SQLite 支持多种 URL 格式作为连接字符串:
简单文件名(如 "myapp.db")必须显式设置 { adapter: "sqlite" },以避免和 PostgreSQL 混淆。
SQLite 支持额外配置选项:
Bun parses query parameters in the URL to set these options:
  • ?mode=ro → readonly: true
  • ?mode=rw → readonly: false, create: false
  • ?mode=rwc → readonly: false, create: true(默认)

插入数据

将 JavaScript 值直接传递给 SQL 模板字面量;Bun 会处理转义。

批量插入

你也可以传递对象数组,Bun 会将其展开为 INSERT INTO ... VALUES ... 语句。

选择插入列

使用 sql(object, ...string) 选择要插入的列。每一列都必须在对象中定义。

查询结果

默认情况下,Bun 的 SQL 客户端将查询结果返回为对象数组,其中每个对象表示一行,并以列名作为键。还提供另外两种格式。

sql``.values() 格式

sql``.values() 方法将每一行返回为值数组,顺序与查询中的列顺序相同。
行的形式如下:
当查询返回重复的列名时,sql``.values() 非常有用。对于对象(默认格式),由于列名是键,最后一个列会覆盖前面的列。使用 sql``.values() 时,每一列都会存在于数组中,因此可以通过索引读取重复列。

sql``.raw() 格式

.raw() 方法将行返回为 Buffer 对象数组。二进制数据处理或追求性能时可以使用它。

SQL 片段

Bun 可以根据运行时条件动态构建查询,同时避免 SQL 注入风险。

动态表名

要动态引用表或模式,请使用 sql() 辅助函数,它会对其进行转义:

条件查询

使用 sql() 辅助函数构建包含条件子句的查询:

更新时动态列

Use sql(object, ...string) to pick which columns to update. Each column must be defined on the object. If you don’t list any columns, Bun uses all keys on the object.

动态值和 WHERE IN

值列表也可以动态创建,用于 WHERE IN 查询。你还可以传入对象数组,并指定用于构建列表的键名。

sql.array 辅助

sql.array 用于将 JS 数组转换为 PostgreSQL 数组字面量:
sql.array 仅支持 PostgreSQL,多维数组和 NULL 元素可能暂不支持。

sql``.simple()

PostgreSQL 协议支持两类查询:“简单”和“扩展”查询。简单查询支持多条语句但不支持参数,扩展查询支持参数但只能一条语句。 要在一次查询中执行多条语句,使用 sql``.simple():
简单查询适用于数据库迁移和设置脚本。 简单查询不能使用参数(${value})。如果需要使用参数,请将查询拆分为多个独立语句。

文件查询

sql.file 从文件中读取查询并执行。如果文件使用 $1 和 $2 之类的占位符,可以向查询传递参数。不传递参数时,文件可以包含多个命令。
使用 SQLite 适配器时,参数也可以是命名参数对象(:name、$name 或 @name 占位符),形式与下文 sql.unsafe 接受的对象相同。

不安全查询

sql.unsafe 执行原始 SQL 字符串。请谨慎使用:它不会转义用户输入。不传递参数时,字符串可以包含多个命令。
使用 SQLite 适配器时,参数也可以是使用 :name、$name 或 @name 占位符的命名参数对象。对象键会保留前缀({ ":id": 1 }),除非连接设置了 strict: true,此时允许使用不带前缀的键:

执行和取消查询

查询是惰性的:只有在使用 await 或通过 .execute() 运行时才会开始执行。 要取消正在运行的查询,请在查询对象上调用 cancel()。

数据库环境变量

你可以使用环境变量配置 sql 连接参数。客户端会按照优先级顺序检查这些变量,并根据连接字符串的格式检测数据库类型。

自动数据库检测

当你不带参数使用 Bun.sql(),或使用带连接字符串的 new SQL() 时,Bun 会根据 URL 格式检测适配器:

MySQL 自动检测

Bun selects MySQL when the connection string matches these patterns:
  • mysql://... — MySQL 协议
  • mysql2://... — MySQL2 协议(兼容别名)

SQLite 自动检测

Bun selects SQLite when the connection string matches these patterns:
  • :memory: — 内存数据库
  • sqlite://... — SQLite 协议
  • sqlite:... — 无斜杠的 SQLite 协议
  • file://... — 文件协议
  • file:... — 无斜杠的文件协议

PostgreSQL 自动检测

当不匹配 MySQL 或 SQLite 格式时,默认使用 PostgreSQL:

MySQL 环境变量

You can configure MySQL connections with environment variables:
Without a connection URL, Bun checks these individual parameters:

PostgreSQL 环境变量

以下环境变量用于定义 PostgreSQL 连接: Without a connection URL, Bun checks these individual parameters:

SQLite 环境变量

You can configure SQLite connections with DATABASE_URL when it contains a SQLite-compatible URL:
Note: Bun ignores PostgreSQL-specific environment variables such as POSTGRES_URL and PGHOST when you use SQLite.

运行时预连接

Bun 可以在应用程序代码运行之前,于启动时预先连接到 PostgreSQL,因此首次查询无需承担连接延迟。
The --sql-preconnect flag establishes a PostgreSQL connection at startup using your configured environment variables. If the connection fails, Bun handles the error without crashing your application.

连接选项

你可以通过向 SQL 构造函数传递选项来手动配置连接。选项因适配器而异:

MySQL 选项

PostgreSQL 选项

SQLite 选项

  • 连接池:SQLite 是文件型数据库,不使用连接池,每个 SQL 实例对应单一连接。
  • 事务:SQLite 支持通过保存点实现嵌套事务,类似 PostgreSQL。
  • 并发访问:SQLite 通过文件锁控制并发,建议使用 WAL 日志模式以提升并发能力。
  • 内存数据库::memory: 创建临时数据库,仅存活于连接生命周期内。

动态密码

对于访问令牌等替代身份验证方案,或使用轮换密码的数据库,请将 password 设置为同步或异步函数。Bun 会在连接时调用该函数以获取密码。

SQLite 特有功能

查询执行

SQLite 同步执行查询,与使用异步 I/O 的 PostgreSQL 不同。API 仍然返回 Promise:

SQLite PRAGMA 语句

使用 PRAGMA 语句配置 SQLite 行为:

数据类型差异

SQLite 类型系统比 PostgreSQL 灵活:

事务

用 sql.begin 启动事务,支持 PostgreSQL 和 SQLite。PostgreSQL 会从连接池中保留连接,SQLite 则在单一连接上开始事务。 Bun sends the BEGIN command automatically, including any optional configurations you specify. If an error occurs during the transaction, Bun issues a ROLLBACK.

基本事务

要在事务中对查询进行流水线处理,请从回调中返回查询数组:

保存点(Savepoints)

保存点会在事务中创建中间检查点,因此可以回滚其中一部分,而不会中止整个事务。

分布式事务

Two-Phase Commit (2PC) is a distributed transaction protocol. In phase 1, the coordinator prepares each node, making sure its data is written and ready to commit. In phase 2, the nodes commit or roll back based on the coordinator’s decision. 在 PostgreSQL 和 MySQL 中,分布式事务会在其原始会话结束后继续存在,因此具有相应权限的用户或协调者可以在之后提交或回滚这些事务。PostgreSQL 将其实现为预备事务;MySQL 使用 XA 事务。 分布式事务期间未捕获的异常会回滚所有更改。否则,你可以在之后提交或回滚该事务。

认证

Bun supports SCRAM-SHA-256 (SASL), MD5, and Clear Text authentication. We recommend SASL for better security. See Postgres SASL Authentication.

SSL 模式概述

PostgreSQL 的 SSL/TLS 模式控制是否需要安全连接,以及执行何种程度的证书验证。

连接字符串中设置

你也可以在连接字符串中设置 SSL 模式:

连接池

Bun’s SQL client manages a connection pool. The pool reuses database connections across queries instead of opening and closing one for each query, and it caps the number of concurrent connections.
Bun doesn’t open a connection until you run a query.

保留连接

sql.reserve() 从连接池中获取一个连接,并返回一个封装该连接的客户端,以便你可以在隔离的连接上运行查询。
When every connection is busy, reserve() waits until one frees up. Pass an AbortSignal to stop waiting: the promise rejects with signal.reason and no connection is taken from the pool. Aborting after the promise resolved has no effect, so a connection you received must still be released.

LISTEN / NOTIFY(PostgreSQL)

sql.listen() 订阅 PostgreSQL 通知频道,sql.notify() 向频道发布通知。共享数据库的进程可以将其用作轻量级消息总线:缓存失效、在插入行时唤醒工作进程(通常由调用 pg_notify 的触发器完成)、分发小型事件。
listen() 会在 PostgreSQL 确认 LISTEN 后完成,因此在它完成后发出的 notify() 都能够送达。它所完成得到的订阅对象也是异步可释放对象:

工作原理

  • All subscriptions on a client share one dedicated connection. The first listen() opens it, and removing the last subscription closes it. As a result, a client that never listens never pays for the connection, and unlistening everything lets the process exit without sql.close().
  • While anything is subscribed, that connection keeps the process alive, like a listening server.
  • If the connection drops, Bun re-establishes it with exponential backoff (250ms doubling to 32s, with jitter) and re-subscribes every channel. PostgreSQL only delivers to connected listeners, so notifications sent in between are lost. The optional third argument to listen() runs on the initial subscribe and after every reconnect, which is the place to catch up:
  • Every listen() call is its own subscription. Several on one channel share a single server-side LISTEN. Each callback receives every notification, and each handle’s unlisten() removes only what its own call registered. A callback (either argument) that throws is reported as an uncaught exception and stays subscribed.
  • Bun quotes channel names as identifiers for you. Like any PostgreSQL identifier, they are limited to 63 bytes, and Bun rejects longer names rather than silently truncating them. PostgreSQL limits payloads to 8000 bytes by default.

notify()

notify() is an ordinary query (SELECT pg_notify($1, $2)) on whichever handle you call it through. On sql it uses the pool. Inside sql.begin() it runs in the transaction, so PostgreSQL delivers it on COMMIT and drops it on ROLLBACK. Calling notify() inside a transaction is how to announce a change only once it is visible:
负载是可选的:sql.notify("cache-invalidated") 就是 PostgreSQL 的裸 NOTIFY。保留连接和事务句柄也具有 listen();它始终使用客户端共享的监听连接。 LISTEN/NOTIFY 仅支持 PostgreSQL;在 MySQL 和 SQLite 上调用这些方法会被拒绝。

预处理语句

By default, Bun’s SQL client creates named prepared statements for queries it can infer are static, which is faster. To disable named prepared statements, set prepare: false in the connection options:
设置 prepare: false 后: 查询仍使用“扩展”协议,但会作为未命名预处理语句运行。未命名预处理语句只会持续到下一条指定将未命名语句作为目标的 Parse 语句发出为止。
  • Parameter binding is still safe against SQL injection
  • The server parses and plans each query from scratch
  • Queries are not pipelined
  • Only one command per query is supported (unless you use sql``.simple())
  • 使用 PGBouncer 事务模式(1.21.0 以后的版本如果配置正确已支持命名语句)
  • 调试查询执行计划
  • 动态 SQL 需频繁重新规划查询
  • 不支持多条语句(除非用 .simple())
  • Using PGBouncer in transaction mode (though since PGBouncer 1.21.0, protocol-level named prepared statements are supported when configured properly)
  • Debugging query execution plans
  • Working with dynamic SQL where query plans need to be regenerated frequently
禁用预处理语句可能会减慢使用不同参数频繁执行的查询,因为服务器需要对每次查询都从头开始解析和规划。

错误处理

客户端为不同的失败场景提供了类型化错误。错误具有数据库特定性,并继承自基础错误类:

错误类示例

PostgreSQL 连接错误

认证错误

查询错误

数据类型错误

The client raises these errors while it decodes a result, after the server ran the query. Only that query rejects. The connection stays open, and the other queries on it continue. An INSERT, UPDATE or DELETE ... RETURNING can already be stored at that point, so do not retry a write blindly. To read the value, cast the column to text or use .raw().

Protocol Errors

事务错误

SQLite 特有错误

SQLite 错误包含 SQLite 的标准错误码和错误号:
错误处理示例:

数字与 BigInt

Bun returns PostgreSQL bigint (int8) values, and MySQL BIGINT values outside the 32-bit range, as strings:

使用 BigInt 代替字符串

要将大数作为 BigInt 而不是字符串获取,请在创建 SQL 客户端时将 bigint 选项设置为 true:

路线图

我们尚未完成的事项:
  • Column name transforms (for example, snake_case to camelCase). This is mostly blocked on a unicode-aware implementation of changing the case in C++ using WebKit’s WTF::String.
  • Column type transforms

数据库特有功能

认证方式

MySQL 支持多种认证插件,自动协商: MySQL supports multiple authentication plugins, which the client negotiates automatically: 客户端自动处理服务器请求切换认证插件,包括非 SSL 下的密码安全交换。 The client automatically handles authentication plugin switching when the server requests it. MySQL 对所有参数化查询使用服务端预处理语句:

多结果集

MySQL 支持多语句查询返回多个结果集:

字符集与排序规则

Bun.SQL 对 MySQL 连接使用 utf8mb4 字符集,涵盖包括表情符号在内的所有 Unicode 字符。

连接属性

Bun 会向 MySQL 发送客户端信息以便进行监控:

类型处理

Bun converts MySQL types to JavaScript types: DATETIME and TIMESTAMP values have no timezone on the wire, so Bun reads them back as UTC. The Date you get has the same UTC wall-clock that was stored, regardless of the machine’s timezone. Reading as UTC matches how Bun writes values (a bound Date stores its UTC components). The same applies to PostgreSQL’s timestamp (without time zone); timestamptz carries an explicit offset and is unaffected. Bun also sets time_zone = '+00:00' on every MySQL connection it opens. The server converts TIMESTAMP columns through the session time zone, so this keeps the stored instant equal to the Date you bound, and it makes server-side time functions such as NOW() and CURRENT_TIMESTAMP evaluate in UTC. There is no option to pick a different session time zone yet: a manual SET time_zone = ... reaches only the one pooled connection that runs it (#40254 tracks a per-connection option).

与 PostgreSQL 的差异

API 是统一的,但行为有所不同:
  1. 参数占位符:MySQL 内部使用 ?,Bun 自动转换 $1、$2 风格占位符
  2. RETURNING 语法:MySQL 不支持 RETURNING,使用 result.lastInsertRowid 或单独 SELECT
  3. 数组类型:MySQL 无原生数组类型,区别于 PostgreSQL

MySQL 特有功能

我们尚未实现对 LOAD DATA INFILE 的支持。

PostgreSQL 特有功能

尚未实现:
  • COPY 支持
未实现的一些较少用特性:
  • GSSAPI 认证
  • SCRAM-SHA-256-PLUS 支持
  • Point 和 PostGIS 类型
  • 多维整数数组(仅支持部分类型)

常见模式与最佳实践

处理 MySQL 结果集

MySQL 错误处理

MySQL 性能优化建议

  1. 使用连接池:根据负载配置合理的 max 连接数
  2. 启用预处理语句:默认开启,提升性能
  3. 批量操作用事务包裹:关联查询放在同个事务里
  4. 合理建立索引:MySQL 查询性能依赖索引策略
  5. 使用 utf8mb4 字符集:默认启用,支持完整 Unicode 字符。

常见问题

最初的计划是添加更多数据库驱动。统一 API 现在支持 PostgreSQL、MySQL 和 SQLite。
Bun detects the adapter automatically from the connection string:
  • 以 mysql:// 或 mysql2:// 开头为 MySQL
  • 匹配 SQLite 格式(:memory:、sqlite://、file://)为 SQLite
  • 其他默认使用 PostgreSQL
支持,包括 OUT 参数和多个结果集:
当然,可以使用所有 MySQL 专属语法:

为什么不直接用已有库?

你也可以在 Bun 中使用 postgres.js、pg 和 node-postgres 等 npm 包。它们都是很好的选择。 但有两个原因:
  1. 我们认为,对于开发者来说,将数据库驱动内置到 Bun 中会更加简单。你花在挑选库上的时间,本可以用来构建应用。
  2. 我们利用了一些 JavaScriptCore 引擎内部机制来更快地创建对象,而这些机制很难在库中实现。

鸣谢

特别感谢 @porsager 的 postgres.js 对 API 接口设计的启发。