Workers Bindings 與 Node.js 相容:用 env 宣告式連接資源、nodejs_compat 用 node: 模組 | Cloudflare 完整教學

2026/08/10
Workers Bindings 與 Node.js 相容:用 env 宣告式連接資源、nodejs_compat 用 node: 模組 | Cloudflare 完整教學

前九篇,我們反覆用 env 存取 KVD1Queue,把它當成理所當然——但這個把 Cloudflare 服務「注入」進 Worker 的機制,正是整個平台威力的核心。這一篇,我們正式拆解 Bindings:什麼是 Binding(在 wrangler.jsonc 宣告式連接資源,不用連線字串、不用金鑰)、env 物件怎麼運作、KV / R2 / D1 / Durable Objects / Queues / AI / Service / Secrets 各種 binding 的全貌、如何用 wrangler types 自動生成 Env interface 型別,以及開啟 nodejs_compat flag 後,哪些 node: 模組能用、哪些只是空殼。也徹底講清楚環境變數 vs Secrets vs Binding 的差別。這一篇同時為我們的 CF-1 運算核心收尾。

前言

上一篇《Cron Triggers 排程》,我們讓 Worker 學會「看時鐘做事」——用 cron 表達式描述時間、匯出 scheduled handler,做出不靠人觸發、時間到就自己跑的定時任務。但你有沒有注意到,無論是 fetchscheduled 還是 WebSocket handler,它們的第二個參數永遠是同一個東西:env。我們一路用 env.SESSIONSenv.DBenv.TASK_QUEUE 存取各種 Cloudflare 服務,卻從沒認真問過:這個 env 到底是什麼?它裡面的 KV、D1、Queue 是怎麼跑進來的?

答案就是本篇的主角——Bindings(綁定)。先給一句話定義:Binding 是 Cloudflare Workers 用來「宣告式連接資源」的機制。你不在程式碼裡放連線字串或 API 金鑰,而是在 wrangler.jsonc 用一段設定「宣告」你要用哪個資源,平台在部署時就把它「綁」到你的 Worker,執行時透過 env 物件直接拿到一個已經接好、免認證的物件。

打個比方:傳統存取資料庫或外部服務,像是「自己開車出門辦事」——你得知道地址(host)、帶好鑰匙與證件(帳密、API Key)、自己開上路(建立連線)、還要在門口通過保全驗證(認證),一趟往返費時費力,鑰匙掉了還會出大事。而 Binding 像是「你家牆上有一排已經接好的水管與電線插座」——你不需要知道自來水廠在哪、也不需要出示證件,只要打開 env.MY_KV 這個「水龍頭」,水就來了。管線的鋪設、驗證、授權,平台在你「宣告」的那一刻就幫你接好了。你只描述「我要接哪個資源」,剩下的交給平台。

這種「你只宣告要什麼、不管怎麼連」的風格,就是所謂的宣告式(declarative)。它帶來三個實際好處:零憑證外洩風險(程式碼裡沒有任何密碼)、零連線樣板(不用寫建立連線與認證的程式)、極低延遲(資源就在 Cloudflare 網路內,無額外網路跳轉)。

讀完本篇,你會掌握:

  • Binding 是什麼、env 如何運作——宣告式連接資源的設計哲學,以及 env 物件如何在 handler 裡注入所有資源
  • 常見 binding 種類概覽——KV / R2 / D1 / Durable Objects / Queues / AI / Service Bindings / Secrets,各自解決什麼問題、wrangler.jsonc 設定鍵是什麼
  • Env interface 與 wrangler types——如何為 env 建立 TypeScript 型別,以及為何該用自動生成而非手寫
  • nodejs_compatnode: 模組——開啟相容性 flag 後,哪些 Node.js 模組完整支援、哪些只是 stub 存根,以及環境變數 vs Secrets vs Binding 的界線

核心概念

Binding 的運作原理:宣告 → 注入

先把整條路徑走一遍,你就懂 Binding 的全貌了。它分成三步:

  1. 宣告(在 wrangler.jsonc):你用一段設定告訴 Cloudflare——「這個 Worker 要用一個 KV namespace,我在程式裡叫它 MY_KV,它的實際 id 是 xxx」。這裡的關鍵是 binding 這個欄位——它就是你之後在程式碼中透過 env 存取時用的變數名。
  2. 綁定(部署時):執行 wrangler deploy 時,Cloudflare 平台讀取這段宣告,把對應的實體資源「掛」到你的 Worker 上,並在你的 Worker 與資源之間建立好一條免認證、走內部網路的通道。
  3. 注入(執行時):每次 handler 被呼叫,平台就把所有 binding 打包成一個 env 物件,當作參數傳進來。於是 env.MY_KV 就是那個接好的 KV 物件,env.DB 就是那個 D1 資料庫,直接呼叫方法即可。

一張圖看懂這個「宣告式連接」:

   wrangler.jsonc(宣告)          Worker 執行時(注入)
 ┌────────────────────────┐      ┌──────────────────────────┐
 │ kv_namespaces:         │      │ fetch(request, env, ctx){│
 │   binding: "MY_KV" ────┼──────┼──► env.MY_KV.get(...)    │
 │   id: "abc123"         │ 綁定 │                          │
 │ d1_databases:          │      │  env.DB.prepare(...)     │
 │   binding: "DB" ───────┼──────┼──►                       │
 └────────────────────────┘      └──────────────────────────┘
        「我要什麼資源」              「直接用,免連線、免金鑰」

對比一下傳統做法,差異就很鮮明:

面向傳統(連線字串 / API Key)Binding(宣告式)
憑證程式碼或環境變數裡有帳密/金鑰無需憑證,不在程式碼裡放任何密碼
連線自己建立連線、通過認證平台接好,直接拿到可用物件
延遲多一趟網路往返 + 認證零額外跳轉,資源就在 CF 網路內
設定位置散落在程式碼各處集中在 wrangler.jsonc

常見 binding 種類概覽

env 上能掛的 binding 種類很多,以下是最常見、你一定會遇到的幾種。本篇只做全景概覽——每一種後續都有專章詳談,這裡先建立地圖:

Binding解決什麼問題env 用法(示意)wrangler.jsonc 設定鍵
KV全球讀取快、最終一致的鍵值儲存env.MY_KV.get / put / delete / listkv_namespaces
R2S3 相容的物件儲存(圖片、檔案),無出口流量費env.MY_BUCKET.get / put / deleter2_buckets
D1SQLite 關聯式資料庫,支援 SQLenv.DB.prepare(...).bind(...).first / rund1_databases
Durable Objects有狀態的協調單元(聊天室、計數器)env.DO.idFromName(...).get(id)durable_objects
Queues非同步訊息佇列,可批次、可重試env.MY_QUEUE.send(...) / queue handlerqueues
Workers AI在邊緣執行 LLM 等 AI 推論env.AI.run(model, inputs)ai
Service BindingsWorker 直接呼叫另一個 Worker(RPC / fetch)env.SERVICE.fetch(...) / 直接方法呼叫services
Secrets加密儲存的機密字串(金鑰、密碼)env.API_KEY(字串)wrangler secret put

有幾個觀念要先建立:

  • KV / R2 / D1 / DO / Queues / AI / Service 這些 binding,你拿到的是一個可以呼叫方法的物件(有 getputsendrunfetch 等方法),它們代表「一個 Cloudflare 服務資源」。
  • Secrets 與環境變數則不同,你拿到的是一個字串值(例如 env.API_KEY 就是那串金鑰本身),它們代表「一個設定值」。
  • 兩者都掛在同一個 env 上、都靠宣告注入,但一個是「服務」、一個是「值」——這個分野後面會反覆用到。

Env interface:給 env 型別

env 很好用,但它有個問題:如果你打錯字寫成 env.MY_KY(把 KV 打成 KY),或忘了某個 binding 的方法名,JavaScript 不會事先告訴你,得等執行時才炸。在 TypeScript 裡,我們用一個 Env interface 來描述 env 上有哪些 binding、各是什麼型別,編輯器就能自動補全、事先抓錯:

// 描述這個 Worker 的 env 上有哪些 binding
interface Env {
  MY_KV: KVNamespace;        // KV binding
  DB: D1Database;            // D1 binding
  MY_BUCKET: R2Bucket;       // R2 binding
  MY_QUEUE: Queue;           // Queues producer binding
  AI: Ai;                    // Workers AI binding
  API_BASE_URL: string;      // 環境變數(明文)
  API_KEY: string;           // Secret(加密)
}

注意:binding(如 MY_KV)的型別是專門的類別(KVNamespaceD1Database 等,來自 Cloudflare 的型別定義),而環境變數與 Secret 的型別就是 string——因為它們本質上就是字串。這再次呼應了上一節「服務物件 vs 設定值」的分野。

手寫 Env 很容易漏、也容易和 wrangler.jsonc 不同步。所以更推薦用指令自動生成:

# 讀取 wrangler.jsonc,自動生成與所有 binding 對應的型別定義
npx wrangler types

它會產生一份型別檔(通常是 worker-configuration.d.ts),裡面就是根據你 wrangler.jsonc 裡所有 binding 生成的 Env 定義。每次改了 binding,重跑一次即可,永遠與設定同步。

實作範例

概念齊了,我們把 binding 宣告、env 存取、型別、以及 nodejs_compatnode: 模組,串成幾個完整可執行的範例。

範例一:宣告多種 binding 並在 env 存取

先在 wrangler.jsonc 一次宣告 KV、D1、R2、以及環境變數:

// wrangler.jsonc
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-08-01",

  // KV binding:程式裡叫 MY_KV
  "kv_namespaces": [
    { "binding": "MY_KV", "id": "<你的 KV namespace id>" }
  ],

  // D1 binding:程式裡叫 DB
  "d1_databases": [
    { "binding": "DB", "database_name": "my-app-db", "database_id": "<你的 D1 id>" }
  ],

  // R2 binding:程式裡叫 MY_BUCKET
  "r2_buckets": [
    { "binding": "MY_BUCKET", "bucket_name": "my-app-storage" }
  ],

  // 環境變數(明文,非機密):程式裡叫 env.API_BASE_URL
  "vars": {
    "API_BASE_URL": "https://api.example.com",
    "ENVIRONMENT": "production"
  }
}

再看 Worker 本體如何透過 env 使用它們——注意程式裡完全沒有任何連線字串、host、帳密:

// src/index.ts —— 透過 env 使用多種 binding
interface Env {
  MY_KV: KVNamespace;
  DB: D1Database;
  MY_BUCKET: R2Bucket;
  API_BASE_URL: string;
  ENVIRONMENT: string;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    // 1) 環境變數:直接當字串用
    console.log(`目前環境:${env.ENVIRONMENT}, API 位址:${env.API_BASE_URL}`);

    // 2) KV binding:免連線、免金鑰,直接呼叫方法
    await env.MY_KV.put("greeting", "Hello from KV");
    const greeting = await env.MY_KV.get("greeting");

    // 3) D1 binding:用 prepared statement 防注入
    const user = await env.DB
      .prepare("SELECT name FROM users WHERE id = ?")
      .bind(1)
      .first<{ name: string }>();

    // 4) R2 binding:讀一個物件
    const object = await env.MY_BUCKET.get("logo.png");
    const hasLogo = object !== null;

    return Response.json({
      environment: env.ENVIRONMENT,
      greeting,
      user,
      hasLogo,
    });
  },
} satisfies ExportedHandler<Env>;

看見了嗎?env.MY_KVenv.DBenv.MY_BUCKET 全都「直接就能用」——沒有 new Client()、沒有 connect()、沒有 API Key。這就是宣告式 binding 的威力:所有連線與授權,平台在你宣告的那一刻就接好了。

範例二:用 Secrets 存放機密(而非明文)

上一個範例的 API_BASE_URL非機密設定,用明文 vars 沒問題。但如果你有一把機密金鑰(例如呼叫第三方 API 的 token),絕對不能寫進 wrangler.jsoncvars,也不能硬寫在程式碼——因為那會進版控、會外洩。正確做法是用 Secret:

# 設定一個加密的 Secret(互動式,會提示你輸入值)
npx wrangler secret put THIRD_PARTY_API_KEY

# CI/CD 環境可從 stdin 餵入
echo "sk-xxxxxxxx" | npx wrangler secret put THIRD_PARTY_API_KEY

# 查看已設定的 Secret 名稱(值看不到,已加密)
npx wrangler secret list

設定後,Secret 的用法和環境變數一模一樣——都掛在 env 上、都是字串:

// src/index.ts —— 用 Secret 呼叫第三方 API
interface Env {
  API_BASE_URL: string;         // 明文環境變數
  THIRD_PARTY_API_KEY: string;  // 加密 Secret
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // env.THIRD_PARTY_API_KEY 由平台安全注入,程式碼裡看不到明文
    const res = await fetch(`${env.API_BASE_URL}/data`, {
      headers: { "Authorization": `Bearer ${env.THIRD_PARTY_API_KEY}` },
    });
    return Response.json(await res.json());
  },
} satisfies ExportedHandler<Env>;

差別只在「哪裡設定」:明文設定進 vars、機密進 wrangler secret put。取用方式對程式碼而言毫無二致。

範例三:開啟 nodejs_compat,用 node: 模組

有時你想用 Node.js 生態裡熟悉的 API,例如 node:cryptocreateHmac,或 node:path 的路徑操作。這需要在 wrangler.jsonc 開啟 nodejs_compat 相容性 flag:

// wrangler.jsonc
{
  "name": "my-worker",
  "main": "src/index.ts",
  // nodejs_compat 需要 compatibility_date 為 2024-09-23 或更新
  "compatibility_date": "2026-08-01",
  "compatibility_flags": ["nodejs_compat"]
}

開啟後,就能 import 那些完整支援node: 模組:

// src/index.ts —— 用 node:crypto 做 HMAC 簽章、node:path 處理路徑
import { createHmac } from "node:crypto";
import path from "node:path";

interface Env {
  HMAC_SECRET: string; // 建議用 wrangler secret put 設定
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // 用 node:crypto 的 createHmac 計算簽章(Node.js 風格 API)
    const body = await request.text();
    const signature = createHmac("sha256", env.HMAC_SECRET)
      .update(body)
      .digest("hex");

    // 用 node:path 組路徑
    const assetPath = path.join("/assets", "images", "logo.png");
    // assetPath === "/assets/images/logo.png"

    return Response.json({ signature, assetPath });
  },
} satisfies ExportedHandler<Env>;

要特別提醒:並不是所有 node: 模組都能真的用。支援程度分三級:

支援程度代表模組能不能用
完整支援node:buffernode:cryptonode:eventsnode:pathnode:streamnode:urlnode:utilnode:zlibnode:http/https可正常使用
部分支援node:osnode:dnsnode:perf_hooksnode:console只有部分功能可用
stub 存根node:child_processnode:worker_threadsnode:vmnode:http2可 import 但呼叫會報錯

那些 stub 存根之所以存在,是為了讓某些只做「這個模組存在嗎」檢查的第三方套件能順利載入——但你不能真的呼叫它們的功能(會拋 not implemented)。原因很單純:Workers 是邊緣的無伺服器環境,天生就沒有「開子行程」「讀寫本機磁碟」「多執行緒」這些概念。

補充:如果你需要 AsyncLocalStorage(不需要整套 Node.js 相容),可以改用更輕量的 nodejs_als flag,啟動更快。

常見錯誤與最佳實踐

坑一:把 Secret、API 金鑰硬寫在程式碼或 wrangler.jsoncvars 裡。

這是最危險、也最常見的錯誤。有人為了圖方便,直接把 const API_KEY = "sk-real-secret" 寫死在原始碼,或塞進 wrangler.jsoncvars。問題是:這兩個地方都會進版控——一旦推上 GitHub,金鑰就等於公開,爬蟲幾分鐘內就會撈走盜用。正確做法:機密一律用 npx wrangler secret put,值會加密儲存、不進版控、設定後也讀不回明文;程式碼裡只透過 env.API_KEY 取用。明文的 vars 只放非機密設定(base URL、環境名、功能開關)。一句話:原始碼與 vars 裡永遠不該出現任何密碼。

坑二:想用 node: 模組,卻忘了開 nodejs_compat(或 compatibility_date 太舊)。

import { createHmac } from "node:crypto",結果部署或啟動時報錯找不到模組——多半是忘了在 wrangler.jsonc"compatibility_flags": ["nodejs_compat"]。另一個常見變體是:flag 加了,但 compatibility_date 早於 2024-09-23,nodejs_compat 也不會生效。正確做法:確認 compatibility_flagsnodejs_compat,且 compatibility_date2024-09-23 或更新。

坑三:以為 nodejs_compat = 完整 Node.js,想用 child_processfs 讀寫檔案。

有人開了 nodejs_compat 就以為 Workers 變成完整 Node.js,興沖沖 import { exec } from "node:child_process" 想開子行程、或用 node:fs 讀寫本機檔案——import 沒報錯(因為是 stub 存根),但一呼叫就拋 not implemented,一頭霧水。正確認知:Workers 是邊緣無伺服器環境,沒有本機檔案系統、不能開子行程、沒有多執行緒。能用的是「純運算 + 標準加密/編碼/串流」這一類(cryptobufferpathstreamzlib…),而不是「開子行程、讀寫磁碟」那一類。要存檔案,用 R2 binding;要跑背景重活,用 QueuesWorkflows——這才是 Workers 的正確姿勢。

坑四:手寫 Env interface,結果和 wrangler.jsonc 不同步。

手寫型別很容易漏一個 binding、或改了設定忘了改型別,導致 env.NEW_KV 明明宣告了卻沒型別、編輯器不補全,甚至誤把型別標錯。正確做法:用 npx wrangler types 自動生成型別——它直接讀 wrangler.jsonc,產出永遠與設定同步的 Env。每次改 binding 就重跑一次,把它加進你的 dev/build 前置步驟,一勞永逸。

坑五:分不清 binding 是「服務物件」還是「設定值」,用錯方法。

有人對 env.API_KEY(Secret,是字串)呼叫 .get(),或把 env.MY_KV(KV,是物件)當字串直接拼接——都會出錯。記住那條分野:KV / R2 / D1 / DO / Queues / AI / Service 這些 binding 給你的是可呼叫方法的物件;環境變數與 Secrets 給你的是字串。用之前先確認手上是「服務」還是「值」。

最佳實踐小結: 記三條——機密一律 wrangler secret put、絕不進版控;要用 node: 模組先開 nodejs_compat + 確認 compatibility_date,且只用得起純運算類 API;型別一律 wrangler types 自動生成、別手寫。守住這三條,你的 binding 就能用得又安全、又順手、又不出錯。

小結

上一篇《Cron Triggers 排程》,我們讓 Worker 學會「看時鐘做事」——用 cron 表達式與 scheduled handler 做出定時任務。這一篇,我們回頭把一路用到的 env 徹底拆開,深入了 Bindings 與 Node.js 相容:

  • Binding 的運作原理——三步走:在 wrangler.jsonc「宣告」→ 部署時「綁定」→ 執行時透過 env「注入」。宣告式連接資源,不用連線字串、不用金鑰、零額外網路跳轉。
  • 常見 binding 種類——KV(鍵值)、R2(物件儲存)、D1(SQL)、Durable Objects(有狀態協調)、Queues(訊息佇列)、Workers AI(推論)、Service Bindings(Worker 互呼)、Secrets(機密字串),各有專屬 wrangler.jsonc 設定鍵,後續都有專章詳談。
  • Env interface 與 wrangler types——為 env 建立型別讓編輯器補全、事先抓錯;binding 是服務類別、環境變數與 Secret 是 string;一律用 npx wrangler types 自動生成、別手寫。
  • nodejs_compatnode: 模組——開 flag(需 compatibility_date ≥ 2024-09-23)後,node:crypto/path/buffer 等完整支援、os/dns 部分支援、child_process/worker_threads 只是 stub 存根;Workers 是邊緣環境,沒有檔案系統與子行程。
  • 環境變數 vs Secrets vs Binding——非機密設定用 vars(明文)、機密字串用 wrangler secret put(加密)、Cloudflare 服務資源用 Binding(物件)。三者都掛 env,但安全性與用途天差地別。

到這裡,CF-1 運算核心正式收尾了——從 Workers 的執行模型、Request/Response、fetch、Cache、Streams、WebSockets、Cron,一路到今天的 Bindings 與 Node.js 相容,你已經完整掌握「Worker 這顆運算核心怎麼運作、怎麼連接周邊資源」。但寫好的 Worker 要怎麼真正跑起來、部署上線?這就要靠 Cloudflare 的官方命令列工具——Wrangler。下一篇《Wrangler 入門》,我們正式進入部署工具的世界,帶你從 wrangler init 建專案、wrangler dev 本地開發、到 wrangler deploy 一鍵上線,把前面學的一切變成活生生跑在邊緣的服務。

想先一睹官方 Bindings 與 Node.js 相容的完整清單,可以隨時參考 Cloudflare 官方 Bindings 文件。運算核心已就緒,我們下一篇《Wrangler 入門》見。

BenZ Software Developer

熱愛技術的軟體開發者,在這裡分享程式開發經驗與學習筆記。

本週主打

AI 自動化入門包

你每天手動在做的那些煩事,其實 AI 可以自己跑。這份給你 10 個照著做就會的自動化工作流 + 50 個複製即用的提示詞,不用會寫程式。

看看這個產品 →