Communitygithub.com

agents-inc/skills

邊緣託管的 SQLite 資料庫,支援 libSQL 驅動程式與嵌入式副本。

skills 是什麼?

這個技能提供了在邊緣或無伺服器環境中使用 Turso/libSQL 資料庫的完整指南。它詳細說明了如何使用 `@libsql/client` 驅動程式,並強調了 `batch()` 相較於 `transaction()` 的優勢,因為 `batch()` 能在單次往返中原子性地執行多條語句,更快速且安全。技能中也明確指出了在無檔案系統的邊緣運行時(如 Cloudflare Workers)必須使用 `@libsql/client/web` 路徑,也說明了各批次操作必須指定交易模式 (`write`, `read`, `deferred`) 的重要性。對於需要極低讀取延遲的場景,技能說明了如何配置嵌入式副本:透過本地檔案 URL 加上 `syncUrl` 來建立實際存於本地的 SQLite 檔案,並與遠端主資料庫同步,實現微秒級讀取。同時也提醒所有寫入最終都會轉發至主節點,因此寫入延遲約為 15-50 毫秒,需要為此進行設計。此技能支援 Node.js、Bun、Deno 等原生環境,以及 Cloudflare Workers、Vercel Edge Functions 等邊緣/無伺服器平台。

相容平台✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/agents-inc/skills/tree/HEAD/src/skills/api-baas-turso

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Turso / libSQL 模式

快速指南: 所有 Turso 資料庫存取都使用 @libsql/client。使用 execute() 進行單一查詢,使用 batch() 進行原子性的多語句操作(優先於互動式交易),只有在後續查詢依賴於先前結果時才使用 transaction()。對於沒有檔案系統存取權限的邊緣/無伺服器運行時,請從 @libsql/client/web 匯入。若要實現零延遲讀取,請使用本地檔案 URL 加上 syncUrl 配置嵌入式副本。所有寫入都會轉發到主資料庫 -- 設計時請考慮 15-50 毫秒的寫入延遲。Turso 底層是 SQLite:單寫入者模型,不支援 ALTER TABLE ... ADD CONSTRAINT,沒有預存程序。


<critical_requirements>

關鍵:使用此技能前請務必注意

所有程式碼都必須遵循 CLAUDE.md 中的專案慣例(kebab-case、命名匯出、匯入順序、import type、命名常數)

(對於多語句的原子性操作,你『必須』使用附帶交易模式的 batch() -- 它比互動式 transaction() 更快且更安全,因為它在單次往返中執行)

(在缺乏檔案系統存取權限的邊緣/無伺服器運行時(Cloudflare Workers、Vercel Edge Functions),你『必須』從 @libsql/client/web 匯入 -- 使用基礎的 @libsql/client 會引入在這些環境中失敗的原生綁定)

(你『必須』將交易模式("write"、"read" 或 "deferred")指定為 batch() 和 transaction() 的第二個參數 -- 預設值為 "deferred",它會以唯讀模式啟動,當進行 INSERT/UPDATE/DELETE 時可能會默默地無法獲取寫入鎖)

(當客戶端在短期程式中不再使用時,你『必須』呼叫 client.close() -- 開放的客戶端會持有連線和檔案控制代碼)

(當客戶端正在執行時,你『絕對不能』直接存取本地嵌入式副本資料庫檔案 -- 同時存取會導致資料損毀)

</critical_requirements>


自動偵測: Turso、libSQL、@libsql/client、createClient、turso.io、嵌入式副本、syncUrl、syncInterval、turso db、turso group、libsql、.turso.io、TURSO_DATABASE_URL、TURSO_AUTH_TOKEN

使用時機:

  • 從任何運行時(Node.js、邊緣、無伺服器)查詢 Turso 託管的 SQLite 資料庫
  • 設定嵌入式副本,以實現從遠端主資料庫同步的零延遲本地讀取
  • 多租戶 SaaS 中的每個租戶一個資料庫(Turso 支援數百萬個資料庫)
  • 需要資料庫但無需連線池複雜性的無伺服器/邊緣函數
  • 使用 batch() 或互動式 transaction() 執行原子性的多語句操作
  • 透過 Turso CLI 管理資料庫群組與多區域部署

不應使用的情況:

  • 需要強烈多寫入者一致性的寫入密集型工作負載(Turso 是單寫入者架構,寫入會轉發到主資料庫)
  • 需要 PostgreSQL 功能(CTE 與變動子查詢、預存程序、進階約束)的複雜關聯式查詢
  • 跨多個資料庫的複雜分散式交易
  • 大型分析資料集(適用 SQLite 的行大小和並發限制)

詳細資源:


哲學

Turso 將 SQLite 帶到邊緣,它將 libSQL(SQLite 的一個分支)作為一項託管服務,並具備多區域複寫能力。@libsql/client 驅動程式提供了一個統一的 API,無論你是連接到遠端 Turso 資料庫、本地 SQLite 檔案、記憶體資料庫,還是從遠端主資料庫同步的嵌入式副本,它的使用方式都完全相同。

核心原則:

  1. 批次優先於交易 -- batch() 在單次往返中發送所有語句,並在隱含交易中執行。互動式 transaction() 需要多次往返並持有資料庫鎖(5 秒逾時)。除非需要在查詢之間使用條件邏輯,否則請使用 batch()。
  2. 寫入永遠打到主資料庫 -- 即使使用嵌入式副本,寫入也會被轉發到遠端主資料庫。寫入延遲取決於與主區域的距離,約 15-50 毫秒。為此設計:樂觀 UI、背景同步、避免寫入密集型熱路徑。
  3. 嵌入式副本用於讀取 -- 從遠端主資料庫同步的本地 SQLite 檔案。讀取時間為微秒級。寫入則轉發到遠端。成功寫入後本地檔案會更新(讀取自己寫入的語意)。
  4. 兩種匯入路徑 -- @libsql/client 包含適用於 Node.js 的原生 SQLite 綁定,並支援 file: URL。@libsql/client/web 是用於邊緣運行時(Cloudflare Workers、Vercel Edge Functions)的純 JS/WASM,且無法開啟本地檔案。
  5. SQLite 語意 -- Turso 就是 SQLite。不支援 ADD CONSTRAINT、沒有預存程序、沒有 LISTEN/NOTIFY、單寫入者 WAL 模式。在選擇 Turso 之前,請先了解 SQLite 的限制。

核心模式

模式 1:客戶端設定

使用 createClient() 建立客戶端。url 決定連線類型:libsql:// 用於遠端 Turso,file: 用於本地 SQLite(僅限 Node.js),:memory: 用於記憶體(測試用)。始終使用環境變數來存放 authToken -- 切勿硬編碼憑證。

有關完整的設定模式,包括單例模組和錯誤範例,請參閱 examples/core.md。


模式 2:執行查詢

execute() 執行單一 SQL 語句。始終使用帶有 args 的參數化查詢 -- 切勿使用字串插值。

// 位置參數:args 為陣列
await client.execute({
  sql: "SELECT * FROM users WHERE id = ?",
  args: [userId],
});

// 具名參數:args 為物件(純名稱會比對 SQL 中的 :name、@name、$name)
await client.execute({
  sql: "INSERT INTO users (name, email) VALUES (:name, :email)",
  args: { name, email },
});

回傳 ResultSet,包含 rows (Array<Row>)、columns、rowsAffected、lastInsertRowid (bigint)。有關型別化結果映射和錯誤範例,請參閱 examples/core.md。


模式 3:批次操作

batch() 在單次往返中原子性地執行多條語句。全部成功或全部復原。務必將交易模式指定為第二個參數。

const results = await client.batch(
  [
    { sql: "INSERT INTO users (name) VALUES (?)", args: ["Alice"] },
    {
      sql: "INSERT INTO audit_log (action, entity_id) VALUES (?, last_insert_rowid())",
      args: ["user_created"],
    },
  ],
  "write", // INSERT/UPDATE/DELETE 必填
);

對變動操作使用 "write",對僅 SELECT 操作使用 "read"(允許在副本上並行執行),使用 "deferred" 以唯讀開始,並在需要時升級。last_insert_rowid() 在同一個批次中的語句間都可以使用。有關大量插入和唯讀批次模式,請參閱 examples/core.md;有關交易模式比較表,請參閱 reference.md。


模式 4:互動式交易

僅在後續查詢依賴於先前查詢的結果時使用 transaction()。它會持有資料庫鎖(5 秒閒置逾時)並需要多次往返。務必使用 try/catch/finally 搭配 close()。

const tx = await client.transaction("write");
try {
  const { rows } = await tx.execute({
    sql: "SELECT balance FROM accounts WHERE id = ?",
    args: [fromId],
  });
  // ... 基於結果的條件邏輯 ...
  await tx.commit();
} catch (error) {
  await tx.rollback();
  throw error;
} finally {
  tx.close();
}

如果所有語句都是預先已知且沒有條件邏輯,請改用 batch()。有關完整的庫存檢查購買範例,請參閱 examples/core.md。


模式 5:不同運行時的匯入路徑

// Node.js、Bun、Deno(具備檔案系統存取、原生綁定)
import { createClient } from "@libsql/client";

// 沒有檔案系統的邊緣/無伺服器運行時(Cloudflare Workers、Vercel Edge)
import { createClient } from "@libsql/client/web";

基礎的 @libsql/client 捆綁了原生 SQLite 綁定,這些綁定在邊緣運行時會失敗。@libsql/client/web 是純 JS/WASM,但無法開啟本地 file: URL。有關完整的 Cloudflare Worker 範例,請參閱 examples/core.md。


模式 6:嵌入式副本

一個從遠端 Turso 主資料庫同步的本地 SQLite 檔案。讀取是本地的(微秒級),寫入則轉發到遠端(15-50 毫秒)。

const client = createClient({
  url: "file:local-replica.db",
  syncUrl: process.env.TURSO_DATABASE_URL,
  authToken: process.env.TURSO_AUTH_TOKEN,
  syncInterval: 60, // 每 60 秒自動同步
});
await client.sync(); // 在第一次讀取前填入本地副本

關鍵點:使用 file: URL 作為本地副本,syncUrl 指向遠端主資料庫,啟動時呼叫 sync(),讀取是本地的,寫入轉發到遠端並具有讀取自己寫入的語意。

使用時機: VM、VPS、容器,或任何具有檔案系統存取的長期運行程序。不適用於沒有檔案系統的無伺服器/邊緣運行時。有關完整模式,包括手動同步、離線模式和加密,請參閱 examples/embedded-replicas.md。


模式 7:資料庫群組與多區域

Turso 將資料庫組織成群組。每個群組都有一個主要區域和可選的副本位置。群組中的所有資料庫都會繼承其位置。

無論哪個副本處理讀取,所有寫入都會傳送到主要區域。新增更多位置可以改善全球的讀取延遲,但不會減少寫入延遲。寫入延遲取決於與主要區域的距離。

有關完整的 Turso CLI 命令參考(turso db create、turso group create、turso db shell 等),請參閱 reference.md。


<decision_framework>

決策框架

batch() 與 transaction() 的選擇

所有 SQL 語句是否預先已知(其間沒有條件邏輯)?
+-- 是 --> 使用 batch()(單次往返、隱含交易、優先選擇)
+-- 否 --> 後續語句是否依賴於先前語句的結果?
    +-- 是 --> 使用 transaction()(互動式、多次往返、持有鎖)
    +-- 否 --> 使用 batch()

匯入路徑選擇

使用什麼運行時環境?
+-- Node.js / Bun / Deno / VM / 容器
|   +-- 需要嵌入式副本(本地檔案)?
|   |   +-- 是 --> 使用 @libsql/client 搭配 file: URL + syncUrl
|   |   +-- 否 --> 使用 @libsql/client 搭配 libsql:// URL
+-- Cloudflare Workers / Vercel Edge / 瀏覽器 / 沒有檔案系統的無伺服器
    +-- 使用 @libsql/client/web(僅限遠端連線,不支援 file: URL)

嵌入式副本與純遠端連線的選擇

程序是否長期運行且具有檔案系統存取?
+-- 是 --> 是否需要亞毫秒級讀取延遲?
|   +-- 是 --> 使用嵌入式副本(file: URL + syncUrl)
|   +-- 否 --> 純遠端連線較簡單(libsql:// URL)
+-- 否(無伺服器、邊緣、短期執行)
    +-- 使用純遠端連線(@libsql/client/web、libsql:// URL)

交易模式選擇

批次/交易將執行哪些操作?
+-- 僅 SELECT 查詢 --> "read"(允許在副本上並行執行)
+-- 任何 INSERT / UPDATE / DELETE --> "write"(取得獨占鎖)
+-- 在呼叫時不確定 --> "deferred"(以唯讀開始,需要時升級)

</decision_framework>


<red_flags>

紅旗警示

高優先級問題:

  • batch()/transaction() 中遺漏交易模式 -- 省略第二個參數會預設為 "deferred",這會以唯讀模式啟動,而對於 INSERT/UPDATE/DELETE 可能會默默地無法獲取寫入鎖。請始終明確指定模式。
  • 在邊緣運行時使用 @libsql/client -- 基礎套件捆綁的原生 SQLite 綁定會在 Cloudflare Workers、Vercel Edge Functions 及類似環境中失敗。請改用 @libsql/client/web。
  • SQL 中的字串插值 -- execute(\SELECT * FROM users WHERE id = '${id}'`)是 SQL 注入漏洞。請始終使用帶有args` 的參數化查詢。
  • 直接存取嵌入式副本檔案 -- 在 libSQL 客戶端執行時,用另一個 SQLite 用戶端開啟本地的 .db 檔案會導致資料損毀。僅能透過客戶端存取。

中等優先級問題:

  • 在 batch() 足以勝任時使用 transaction() -- 互動式交易會持有資料庫鎖並有 5 秒逾時,需要多次往返,並會封鎖其他寫入者。對於預先確定的語句組,請使用 batch()。
  • 未呼叫 client.close() -- 在短期程序中(CLI 腳本、測試清理),忘記關閉客戶端會留下開放的連線和檔案控制代碼。
  • 忽略嵌入式副本的寫入延遲 -- 讀取是微秒級(本地),但寫入需要 15-50 毫秒(轉發到遠端主資料庫)。請相應地設計 -- 避免緊湊的寫入迴圈。
  • syncInterval 設定過低 -- 每次同步會拉取所有已變更的頁框(每個 4KB)。在寫入密集的資料庫上,低於秒級的間隔會產生顯著的網路和 I/O 開銷。

常見錯誤:

  • 套件名稱錯誤 -- 套件名稱為 @libsql/client,不是 libsql-client、@turso/client 或 turso-client。
  • args 物件中的具名參數前綴 -- Args 使用純名稱:{ name: "Alice" } 會比對 SQL 中的 :name、@name 和 $name。請勿包含前綴:{ ":name": "Alice" } 將無法比對。
  • 預期 lastInsertRowid 是數字 -- 它是 bigint | undefined。如果你需要數字,請明確轉換:Number(result.lastInsertRowid),但要注意非常大的 rowid 可能會損失精度。
  • 對原子性操作使用 executeMultiple() -- executeMultiple() 執行原始 SQL 文字(以分號分隔),沒有參數化也沒有隱含交易。請使用 batch() 來執行原子性的參數化操作。

陷阱與邊際案例:

  • batch() 中的語句共用一個交易,但「並非」平行執行 -- 它們會順序執行。

</red_flags>

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

相關技能