以柔和扁平插畫呈現 Cloudflare D1:Worker 透過 Binding 將資料卡送進邊緣資料庫

Cloudflare D1 入門:讓 Worker 讀寫第一個 SQL 資料庫

Cloudflare D1 是什麼?本文從 SQLite、Binding 與 Wrangler 開始,帶你建立資料表、讓 Worker 讀寫資料,並理解本機開發與 migrations。

如果前一篇文章讓你理解 Workers 如何處理 HTTP 請求,那下一個自然問題就是:資料要放在哪裡?

Cloudflare D1 正是用來回答這個問題的產品。小編先給結論:D1 是 Cloudflare 的 Serverless SQL 資料庫,使用 SQLite 熟悉的 SQL 語意;Worker 透過 Binding 取得資料庫連線,再用準備好的 SQL statement 讀寫資料。

這讓你可以從一個很小的 API 開始,逐步建立具備資料持久性的 Cloudflare 應用程式。

D1 適合處理什麼資料?

D1 適合有表格、欄位、條件查詢與關聯需求的資料,例如:

  • 使用者、文章、訂單或留言
  • 需要篩選、排序與分頁的資料
  • 需要唯一值、索引或外鍵的資料
  • Worker API 或管理介面的主要業務資料

可以用一個簡單的方式區分 Cloudflare 的幾種資料服務:

  • D1:以列與關聯為主的 SQL 資料
  • KV:以 Key-Value 讀取為主的資料
  • R2:圖片、影片、備份等物件檔案

這不是在比較誰「比較好」,而是先看資料本身的形狀。若你需要執行 SQL 查詢,D1 才是自然的起點。

先建立 D1 的心智模型

D1 和 Worker 之間不是透過你在程式碼中手動建立的資料庫網路連線來溝通,而是透過 Binding 連接。

這裡有兩個容易混淆的名稱:

  • database_name:Cloudflare 上資料庫的名稱,例如 notes-db
  • binding:Worker 程式中使用的名稱,例如 DB

當 binding 設定為 DB,程式就會從 env.DB 取得 D1Database 物件。這種設計把部署設定與程式碼分開,也避免把連線資訊硬編碼在原始碼裡。

建立第一個 D1 資料庫

在 Worker 專案中執行:

npx wrangler@latest d1 create notes-db

Wrangler 會建立遠端 D1 資料庫,並回傳資料庫 ID 與 Binding 設定。新版流程也可能詢問是否要由 Wrangler 自動把 Binding 加入設定檔;第一次練習時可以選擇讓它代為加入。

如果需要手動設定,Wrangler 設定檔中的 D1 部分大致如下:

{
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "notes-db",
      "database_id": "<由 Wrangler 回傳的資料庫 ID>"
    }
  ]
}

設定完成後,Worker 就能透過 env.DB 存取這個資料庫。

建立資料表

先建立一個 schema.sql,內容只包含一張簡單的 notes 表:

CREATE TABLE IF NOT EXISTS notes (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);

第一次練習建議先在本機資料庫執行:

npx wrangler d1 execute notes-db --local --file=./schema.sql

npx wrangler d1 execute notes-db --local --command="SELECT * FROM notes;"

--local 會使用 Wrangler 的本機開發資料庫,不會直接修改部署中的遠端資料庫。若之後要對遠端資料庫執行 SQL,必須明確使用 --remote;實際操作前要先確認目前的資料庫名稱與環境,避免把測試資料寫進正式環境。

讓 Worker 讀寫 D1

下面是一個最小的 TypeScript Worker:

interface Env {
  DB: D1Database;
}

export default {
  async fetch(request, env): Promise<Response> {
    const url = new URL(request.url);

    if (request.method === "GET" && url.pathname === "/notes") {
      const { results } = await env.DB
        .prepare(
          "SELECT id, title, created_at FROM notes ORDER BY id DESC",
        )
        .all();

      return Response.json(results);
    }

    if (request.method === "POST" && url.pathname === "/notes") {
      const body = (await request.json()) as { title?: unknown };
      const title = typeof body.title === "string" ? body.title.trim() : "";

      if (!title) {
        return Response.json(
          { error: "title is required" },
          { status: 400 },
        );
      }

      const result = await env.DB
        .prepare("INSERT INTO notes (title) VALUES (?)")
        .bind(title)
        .run();

      return Response.json(
        { id: result.meta.last_row_id, title },
        { status: 201 },
      );
    }

    return new Response("Not Found", { status: 404 });
  },
};

這個 Worker 提供兩個路徑:

  • GET /notes:查詢最新的 notes
  • POST /notes:接收 title,寫入一筆資料

D1 的使用流程可以拆成三步:

  1. prepare:準備 SQL statement。
  2. bind:把外部輸入作為參數傳入。
  3. all 或 run:執行查詢並取得結果。

這裡使用問號與 bind,而不是把使用者輸入直接串接到 SQL 字串。除了讓程式更容易閱讀,也能避免把未經處理的輸入當成 SQL 語法執行。

範例為了保持短小,省略了 JSON 格式錯誤、錯誤日誌、驗證規則與認證。正式 API 還需要補上這些邊界處理。

本機與遠端環境要分清楚

D1 開發時最容易發生的問題,不是 SQL 寫錯,而是把指令執行在錯的環境。

可以先記住這個區分:

  • --local:操作本機 D1,適合開發與測試
  • --remote:操作 Cloudflare 上的遠端 D1,會影響部署中的資料

Worker 使用 wrangler dev 執行時,通常會搭配本機 D1。部署後的 Worker 則會使用遠端 Binding。兩者都能叫做 DB,但資料並不是同一份。

因此,建議先在本機建立表格與測試資料,確認 API 行為後,再處理遠端資料庫的 schema 與部署流程。

專案成長後使用 migrations

schema.sql 適合第一次體驗,但正式專案通常要把 schema 變更記錄在版本控制中。D1 提供 migrations,把每次資料庫結構變更保存成有順序的 SQL 檔案。

建立一個 migration:

npx wrangler d1 migrations create notes-db create_notes_table

接著把 CREATE TABLE 等 SQL 寫入 migrations 目錄中產生的檔案,再分別套用到本機與遠端環境:

npx wrangler d1 migrations apply notes-db --local

npx wrangler d1 migrations apply notes-db --remote

這樣資料庫結構就能和應用程式程式碼一起審查、追蹤與部署。正式環境不要只依賴手動貼到 Dashboard 的 SQL;把結構變更寫成 migration,通常更容易重現與回溯。

什麼時候使用 batch?

如果一次請求需要執行多個 SQL statement,可以考慮 D1Database 的 batch 方法。它能把多個 statement 放進一次資料庫呼叫,減少網路往返;D1 會依序執行這些 statement,並把結果按照原本的順序回傳。

這是進一步改善效能與處理多筆相關變更的工具,但第一次接觸 D1 時,先熟悉單一 prepare、bind 與執行流程即可。

D1 的邊界

D1 讓 Worker 使用 SQL 很方便,但它不代表所有資料庫問題都自動消失。設計時仍要注意:

  • 查詢是否有合適的索引
  • 資料庫位置與使用者位置是否造成延遲
  • API 是否需要認證、授權與輸入驗證
  • 讀寫量與查詢複雜度是否符合目前的資料庫選擇
  • 本機、預覽與正式環境是否使用正確的資料庫

如果應用需要非常複雜的分析查詢、特定既有資料庫能力,或必須沿用公司現有資料庫,也可以評估其他 Cloudflare 連線與資料服務。D1 的價值在於:對許多 Workers 應用來說,它提供了一個容易開始、又保留 SQL 思維的資料層。

小編建議的練習順序

  1. 建立 notes 表並用 Wrangler 執行一個 SELECT。
  2. 讓 Worker 透過 DB Binding 查詢資料。
  3. 使用 prepare 與 bind 新增一筆資料。
  4. 把 schema.sql 改成 migration。
  5. 再加入索引、分頁、認證與錯誤處理。

不要一開始就把 D1、KV、R2 與 Durable Objects 全部放進同一個專案。先讓一個小型 API 正確讀寫一張表,再根據資料形狀與流量需求擴充,通常更容易理解每個 Cloudflare 產品的責任邊界。

結語

Cloudflare D1 可以把熟悉的 SQLite/SQL 開發方式帶進 Workers 應用。它的核心不只是「有一個資料庫可以用」,而是透過 Binding、prepared statement 與 migrations,把執行環境、資料存取與 schema 變更組合成一條清楚的開發流程。

如果你已經完成前一篇的 Workers 入門,現在可以試著把 /health API 改成 /notes,讓回應不再是固定文字,而是來自 D1 的實際資料。

延伸閱讀:

No comments yet