news 2026/9/15 21:27:18

@effect/sql-libsql 实战指南:用 Effect SQL 统一访问 libSQL 本地与云端数据库

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
@effect/sql-libsql 实战指南:用 Effect SQL 统一访问 libSQL 本地与云端数据库

@effect/sql-libsql 实战指南:用 Effect SQL 统一访问 libSQL 本地与云端数据库

【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code

导读

@effect/sql-libsql是 Effect 生态中面向 libSQL、LibsqlMigrator.ts)与集成测试(Client.integration.test.ts)展开讲解,读完你将掌握:安装接入方式、完整的客户端配置参数语义、两种客户端构造方式(托管 vs 调用方持有)、事务与保存点机制、SQLite 整数模式的选择,以及如何用同一套迁移与查询 API 同时服务本地文件和远程 Turso 数据库。

一、什么是@effect/sql-libsql

libSQL 是 SQLite 的开放分支,兼容 SQLite 文件格式的同时扩展了嵌入式复制、远程同步等能力,Turso 等云服务即构建在其上。@effect/sql-libsql的角色是一个libSQL 适配器(adapter):它把@libsql/client的底层 API 桥接到 Effect SQL 的通用客户端服务SqlClient上。

从源码注释(LibsqlClient.ts)可以确认它的关键设计承诺:

  • 复用 Effect SQL 的 SQLite 编译器:SQL 模板字符串经由Statement.makeCompilerSqlite编译,行为与 Effect 生态内其他 SQLite 驱动保持一致;
  • 支持托管 SDK 客户端或调用方持有的 live 客户端两种模式;
  • 统一错误分类:libSQL 与 SQLite 的失败都会被归类为SqlError
  • 提供带保存点(savepoint)的事务支持
  • 未实现流式查询executeStream直接抛出"executeStream not implemented"(源码),这是使用前需要明确的边界。

包的入口通过 barrel 文件导出两个命名空间:LibsqlClient(客户端主体)与LibsqlMigrator(迁移支持),见 index.ts。

二、安装与版本要求

README 给出的安装命令为:

npm install effect@rc @effect/sql-libsql@rc

结合 package.json 可补充两点前提:

  1. effectpeerDependency"effect": "workspace:^"),因此必须与应用中已有的 Effect 版本配对安装,一般建议与 Effect 的 RC 版本线保持一致;
  2. 包自身的运行时依赖仅有一个:"@libsql/client": "^0.17.4";开发/测试阶段额外依赖testcontainers(用于拉起 libSQL 服务端做集成测试)。

如果你使用 pnpm 工作区,也可以将两个依赖一并加入工作区后统一安装。由于当前仓库中该包以4.0.0-rc.112版本存在,且源码内所有导出均标注@since 4.0.0,本文示例均基于 Effect v4 /@effect/sql-libsqlv4 RC 语义。

三、客户端配置全解

LibsqlClientConfig是一个联合类型,分为两种形态(源码):基于连接参数的Full,和基于已有 SDK 实例的Live。两种形态共享一组Base选项。

3.1 共享选项(Base)

选项类型说明
spanAttributesRecord<string, unknown>附加到追踪 span 的键值属性;make内部会自动追加["db.system.name", "sqlite"](源码)
transformResultNames(str: string) => string对查询结果列名做转换(如 snake_case → camelCase),通过Statement.defaultTransforms作用于每一行结果
transformQueryNames(str: string) => string对查询语句中的标识符名做转换,用于构建 SQLite 编译器

3.2 连接选项(Full)

Full形态传入 URL 与凭证,客户端由make内部创建并在作用域结束时自动关闭。核心参数:

  • url(必填):数据库地址,支持libsql:http:/https:ws:/wss:file:协议,即可以指向本地文件、远端 HTTP 端点或 WebSocket 端点;
  • authToken:数据库认证令牌,类型为Redacted.Redacted,底层会在调用Libsql.createClient前通过Redacted.value解封(源码);
  • encryptionKey:数据库加密密钥,同样以Redacted包裹;
  • syncUrl:远程同步服务器地址,用于嵌入式副本场景;
  • syncInterval:同步间隔(秒);
  • tls:仅对libsql:URL 生效,默认开启 TLS,设为false可关闭;
  • intMode:SQLite 整数 → JavaScript 值的转换策略,取值"number"(默认)、"bigint""string"。默认number为双精度浮点,无法精确表示绝对值大于2^53 - 1的整数,读取此类大整数会抛RangeErrorbigint可精确表示全部 SQLite 整数;string则原样输出字符串;
  • concurrency:并发上限,默认最多 20 个并发请求,设0表示不限制并发。

一个同时使用本地文件与远程副本的示例配置:

import { Redacted } from "effect" import { LibsqlClient } from "@effect/sql-libsql" const config: LibsqlClient.LibsqlClientConfig = { url: "file:local.db", authToken: Redacted.make("your-token"), syncUrl: "libsql://your-db.turso.io", syncInterval: 60, intMode: "bigint", concurrency: 20 }

3.3 Live 客户端选项(Live)

当你的应用已经在别处创建了@libsql/client的实例时,可传入liveClient复用:

import * as Libsql from "@libsql/client" import { LibsqlClient } from "@effect/sql-libsql" const sdk = Libsql.createClient({ url: "file:local.db" }) const client = LibsqlClient.make({ liveClient: sdk })

注意关键的所有权约定:liveClient由调用方持有,Effect 客户端关闭时不会关闭它(源码注释与makeacquireRelease仅针对自建 SDK 分支可见,见 LibsqlClient.ts)。适合与现有代码共享连接池的场景。

四、三种构造方式:make/layer/layerConfig

LibsqlClient模块提供三种入口:

  1. make(options):最底层构造器,返回Effect<LibsqlClient, never, Scope | Reactivity>,需要Effect.scoped或由 Layer 提供作用域才能安全使用(源码);
  2. layer(config):接收具体配置对象,产出同时提供LibsqlClient与通用SqlClient两个服务的 Layer(源码);
  3. layerConfig(config):接收EffectConfig描述,适合从环境变量/配置文件动态读取配置,失败类型为Config.ConfigError(源码)。

两种 Layer 都在内部Layer.provide(Reactivity.layer),说明该客户端依赖 Effect 的 Reactivity 基础设施。

标准用法(应用入口组装依赖):

import { Layer, Effect } from "effect" import { LibsqlClient } from "@effect/sql-libsql" const SqlLive = LibsqlClient.layer({ url: "libsql://your-db.turso.io", authToken: Redacted.make(process.env.TURSO_AUTH_TOKEN!) }) const program = Effect.gen(function*() { const sql = yield* LibsqlClient.LibsqlClient const rows = yield* sql`SELECT * FROM users` return rows }) program.pipe( Effect.provide(SqlLive), Effect.runPromise )

从服务接口可以看到LibsqlClient通过Context.Service注册、携带运行时类型标记"~@effect/sql-libsql/LibsqlClient",并暴露config字段以便应用在运行期读取实际生效的配置(源码)。

五、SQL 执行 API 与类型化查询

客户端内部通过LibsqlConnectionImpl包装 SDK 的execute,并实现 Effect SQL 的Connection接口(源码)。你可以像使用其他 Effect SQL 驱动一样使用模板字符串查询:

// 插入 yield* sql`INSERT INTO test (name) VALUES ('hello')` // 查询并自动获得对象数组 const rows = yield* sql`SELECT * FROM test` // 列值数组形式(values) const values = yield* sql`select * from test`.values // 原始结果(raw),返回 SDK 的完整结果对象 const raw = yield* sql`INSERT INTO test (name) VALUES ('hello')`.raw

以上用法均有集成测试佐证(Client.integration.test.ts):普通查询返回[{ id: 1, name: "hello" }]这样的对象数组;.values返回[[1, "hello"]].raw返回包含columnscolumnTypesrowsrowsAffectedlastInsertRowid的完整结果对象(lastInsertRowid在 SDK 中以字符串呈现,如"1")。

在执行层,所有失败都会经过classifySqliteError被包装为SqlError,错误信息如"Failed to execute statement"/"Failed to begin transaction"会附带操作名称(源码)。

5.1 与 SqlResolver 组合

@effect/sql-libsql客户端同样可以作为SqlResolver的执行后端,实现 N+1 查询合并。集成测试展示了三种 Resolver 形态(Resolver.integration.test.ts):

  • SqlResolver.ordered:把并发请求批量合并为一次INSERT ... RETURNING *,并严格按请求顺序返回结果;结果数量不匹配时抛出SqlError.ResultLengthMismatchactual/expected字段标明差异);
  • SqlResolver.grouped:按RequestGroupKey/ResultGroupKey分组执行WHERE name IN (...),未命中的请求得到Cause.NoSuchElementError
  • SqlResolver.findById:以 ID 为键做单值查找,同样以NoSuchElementError表达缺省。

这一组合让「libSQL 后端 + 类型安全的数据访问」从 SQL 模板提升到带 Schema 校验的批量请求层。

六、事务与保存点(Savepoint)

make使用 Effect SQL 的Client.makeWithTransaction构建事务支持(源码),底层机制如下:

  • 通过信号量(Semaphore)串行化事务获取:Semaphore.make(1),保证同时只有一个写事务;
  • acquireConnection在获取连接时立即调用 SDK 的transaction("write")开启写事务;
  • 保存点命名规范SAVEPOINT effect_sql_${id}/ROLLBACK TO SAVEPOINT effect_sql_${id},由 Effect SQL 通用层维护嵌套层级。

因此你可以在 libSQL 上使用统一的事务 API,并天然支持嵌套事务:

// 单层事务,失败自动回滚 yield* sql.withTransaction( sql`INSERT INTO test (name) VALUES ('hello')` ) // 嵌套事务(内层失败仅回滚内层保存点) yield* stmt.pipe( Effect.andThen(() => stmt.pipe(sql.withTransaction)), sql.withTransaction )

集成测试验证了事务的关键语义(Client.integration.test.ts):内层Effect.fail("boom")后外层表内无残留数据(整体回滚);嵌套事务中内层失败时,外层提交的内容依然保留(total_rows为 1)。此外还有一个专门的回归测试「releases transaction serialization after begin fails」,确认begin失败时事务串行化信号量会被正确释放,不会阻塞后续事务(同文件 L19-L49)。

七、迁移:LibsqlMigrator

LibsqlMigrator.ts 将 Effect SQL 的通用迁移器适配到 libSQL,完整 re-export 了effect/unstable/sql/Migrator的加载器与错误类型,并提供两个入口:

  • run(options):基于当前SqlClient执行待应用的迁移,返回ReadonlyArray<[id: number, name: string]>,即已应用迁移的 ID 与名称;失败类型为MigrationError | SqlError
  • layer(options):在 Layer 构建期间执行迁移,本身不提供任何服务(Layer<never, ...>),适合放在应用依赖图的“启动阶段”。

迁移选项MigratorOptions来自共享 Migrator 模块(Migrator),通常包含 loader(从目录/内联数组加载 SQL 文件)、迁移表名等。典型用法:

import { LibsqlMigrator } from "@effect/sql-libsql" import * as Migrator from "effect/unstable/sql/Migrator" const MigrateLive = LibsqlMigrator.layer({ loader: Migrator.fromFileSystem("migrations") }) // 在提供 SqlLive 之后组装 program.pipe( Effect.provide(SqlLive), Effect.provide(MigrateLive) )

八、测试基础设施:用 Testcontainers 拉起真实 libSQL 服务

仓库内的集成测试给出了一个可复制的测试模式(util.ts):使用testcontainers启动官方ghcr.io/tursodatabase/libsql-server:main镜像,以SQLD_NODE=primary环境变量 +sqld --http-listen-addr 0.0.0.0:8080命令暴露 HTTP 端点,轮询http://host:8080就绪后,用LibsqlClient.layer({ url })组装测试用 Layer。

const layerClient = Layer.unwrap( Effect.gen(function*() { const container = yield* LibsqlContainer return LibsqlClient.layer({ url: `http://${container.getHost()}:${container.getMappedPort(8080)}` }) }) )

结合@effect/vitestlayer(...)((it) => { it.effect(...) })写法,可以为每个测试用例注入真实的 libSQL 数据库,验证 SQL 执行、事务与 Resolver 行为。若需要本地无容器环境,也可用url: "file:test.db"走嵌入式路径(需 SDK 支持)或使用liveClient注入 mock 实例——测试中「begin 失败释放信号量」的用例正是通过手工构造的liveClient完成的。

九、已知边界与注意事项

  • 不支持流式查询executeStream未实现,需要逐行消费大结果集时应改用其他驱动或分页查询;
  • intMode的精度取舍:默认"number"下读取超过2^53 - 1的整数会抛RangeError,涉及雪花 ID、大整数主键时应显式选择"bigint"
  • 并发上限:默认 20,高吞吐场景可按需调大或置 0 关闭限制;
  • Redacted 凭证authToken/encryptionKey应使用Redacted包裹,避免在日志或追踪中泄露;
  • Live 客户端所有权:传入liveClient后关闭由调用方负责,使用作用域化的make时则自动释放;
  • 版本配套@effect/sql-libsqlv4 面向 Effect v4(RC),使用时请保持effect@rc@effect/sql-libsql@rc同线安装,避免与旧版 Effect 混用。

结语

@effect/sql-libsql把 libSQL 的本地与云端能力无缝接入 Effect SQL:一份配置、一套模板字符串 API、统一的事务与迁移语义,即可同时覆盖file:本地库与libsql:/http:/ws:远端库。结合layerConfig的环境变量驱动、SqlResolver的批量请求优化,以及 Testcontainers 的真实数据库集成测试,它可以作为 Effect 应用中类型安全、可测试、可观测的持久层基础组件。若你正在 Effect v4 项目中规划数据层,可以直接从 LibsqlClient.ts 与 Client.integration.test.ts 起步,将其作为接入与测试的参考蓝本。

【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 21:25:35

大模型学习指南:从入门到精通的系统路线

1. 为什么你需要这份大模型学习指南去年有个做Java开发的朋友找我聊天&#xff0c;说想转行做大模型方向&#xff0c;结果在B站看了两周视频后更迷茫了。这让我意识到&#xff0c;很多想入行AI领域的人&#xff0c;最缺的不是学习资源&#xff0c;而是一张清晰的路线图。这份指…

作者头像 李华
网站建设 2026/9/15 21:24:37

NDSS 2026中段论文:网络安全技术路线图与工业复现指南

1. 这份“NDSS 2026论文清单&#xff08;中&#xff09;”到底是什么&#xff0c;以及它为什么值得你花时间细读很多人看到“NDSS 2026论文清单及摘要&#xff08;中&#xff09;”这个标题&#xff0c;第一反应是&#xff1a;又一份学术会议论文合集&#xff1f;点开看看标题就…

作者头像 李华
网站建设 2026/9/15 21:23:32

YooAsset资源管理系统:Unity热更新与包体优化实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 21:22:06

如何为 Prefect Cloud 配置 SSO 单点登录

如何为 Prefect Cloud 配置 SSO 单点登录 【免费下载链接】prefect Prefect is a workflow orchestration framework for building resilient data pipelines in Python. 项目地址: https://gitcode.com/GitHub_Trending/pr/prefect 如果你的团队已经把身份管理&#xf…

作者头像 李华
网站建设 2026/9/15 21:20:57

基于OpenCV的弱光图像增强算法实现与优化

1. 项目背景与核心需求在计算机视觉和图像处理领域&#xff0c;弱光环境下的图像增强一直是个经典难题。无论是安防监控、医疗影像还是移动摄影&#xff0c;我们经常会遇到因光照不足导致的图像质量下降问题。传统解决方案往往面临细节丢失、噪声放大或色彩失真的困扰。这个项目…

作者头像 李华
网站建设 2026/9/15 21:19:36

2026年AI论文写作工具核心功能与应用解析

1. 为什么我们需要专业AI论文写作工具作为一名科研工作者&#xff0c;我深刻理解论文写作过程中的痛点。从文献综述到实验设计&#xff0c;从数据分析到论文撰写&#xff0c;每个环节都需要耗费大量时间和精力。特别是在2026年这个时间节点&#xff0c;学术竞争愈发激烈&#x…

作者头像 李华