課程大綱
版本基準:2026 年 7 月。所有 API、設定鍵名、產品狀態皆以此時間點的官方文件為準。 目標讀者:有經驗的全端開發者(熟 TypeScript / REST / SQL,但沒碰過 Cloudflare)。 結構:單一貫穿專案 + 各章獨立最小範例。
一、課程設計原則
Section titled “一、課程設計原則”1.1 讀者假設
Section titled “1.1 讀者假設”| 假設會 | 不假設會 |
|---|---|
| TypeScript、ES modules、npm/pnpm | Cloudflare 任何產品 |
| HTTP 語意、REST、JSON | V8 isolate 執行模型 |
| SQL(SELECT / JOIN / index) | SQLite 的邊緣特性 |
| Git、基本 CI 概念 | Wrangler / miniflare / workerd |
| Docker 基本操作 | Actor model / durable execution |
每篇開頭固定標示「前置章節」,允許讀者跳讀。
1.2 三層內容結構
Section titled “1.2 三層內容結構”每一篇文章都由三塊組成,比例大約 4:4:2:
- 心智模型(Why) — 這個 primitive 解決什麼問題、它的物理限制是什麼。對有經驗的開發者,這是最有價值的部分,也是 AI 生成內容最容易略過的部分。
- 獨立最小範例(How) — 一個 30–80 行、可以單獨
git clone跑起來的 demo,不依賴貫穿專案。 - 貫穿專案進度(Real) — 把該章的 primitive 接進 LinkForge,處理真實世界的邊角:錯誤、重試、成本、多租戶隔離。
1.3 每篇固定小節
Section titled “1.3 每篇固定小節”## 前置章節## 這篇要解決的問題## 心智模型## 動手做:最小範例## 接進 LinkForge## 限制與計費(Limits & Pricing)## 常見陷阱(Gotchas)## 延伸閱讀「限制與計費」與「常見陷阱」是本系列的差異化重點。Cloudflare 官方文件把 limits 放在獨立頁面,多數教學完全不提,導致讀者在 production 才踩雷。
1.4 寫作規範
Section titled “1.4 寫作規範”- 專業名詞一律保留英文:
Durable Object、binding、isolate、eventual consistency、cold start。不翻譯成「持久物件」。 - 程式碼、註解、檔名、commit message 一律英文。
- 每個 code block 標註可執行的最小前置條件(wrangler 版本、compatibility date)。
- 所有計費數字標註查證日期,並提醒讀者以官方 pricing 頁為準。
二、貫穿專案:LinkForge
Section titled “二、貫穿專案:LinkForge”多租戶短網址 + 即時點擊分析平台。
選它的理由:短網址是極端讀多寫少、天然多租戶、天然事件流的工作負載,正好逐一逼出 Cloudflare 每個 primitive 的存在理由,而不是為了用而用。
2.1 最終架構
Section titled “2.1 最終架構” ┌─────────────────────────────┐ go.lnk.fo/abc ──▶ │ Worker: redirector │ │ KV lookup → 302 │──▶ Queue: click-events └─────────────────────────────┘ │ ▼ app.lnk.fo ──▶ Worker: dashboard (React Router v8) Worker: click-consumer │ │ ├─ D1 (Drizzle) : tenants, links, users │├─▶ Analytics Engine ├─ Durable Object: LinkCounter (即時計數) │├─▶ D1 rollup ├─ Durable Object: LiveDashboard (WS) │└─▶ Pipelines → R2 (Iceberg) ├─ R2 : CSV/QR/OG image │ ├─ Workflow : weekly report + email │ ├─ Workers AI : 惡意連結掃描、摘要 │ └─ Vectorize : 連結語意搜尋 lnk.fo (行銷/文件) ──▶ Astro on Workers2.2 各章交付物累積表
Section titled “2.2 各章交付物累積表”| Part | LinkForge 進度 |
|---|---|
| 1 | 可部署的 redirect Worker(硬編碼對照表)+ Hono API 骨架 |
| 2 | KV 熱路徑查詢 + D1/Drizzle 資料模型 + R2 匯出 |
| 3 | DO 即時計數器 + WebSocket 即時看板 + alarm 聚合 |
| 4 | Queue 點擊事件管線 + Analytics Engine 分析後台 + Workflow 週報 |
| 5 | React Router v8 管理後台 + Astro 行銷站 + 完整 auth |
| 6 | 自訂網域、OG 圖片自動截圖、寄信、多租戶隔離 |
| 7 | AI 惡意連結偵測 + 語意搜尋 + MCP server |
| 8 | 測試套件、observability、CI/CD、成本儀表板 |
2.3 Repo 結構(monorepo, pnpm workspace)
Section titled “2.3 Repo 結構(monorepo, pnpm workspace)”linkforge/├── apps/│ ├── redirector/ # 熱路徑 Worker,刻意保持極簡│ ├── api/ # Hono API + DO + Workflows│ ├── dashboard/ # React Router v8 on Workers│ ├── site/ # Astro on Workers│ └── consumers/ # Queue consumer Worker├── packages/│ ├── db/ # Drizzle schema + migrations│ ├── shared/ # 共用型別、zod schema│ └── config/ # tsconfig / eslint 基底└── examples/ # 各章獨立範例,ch03/ ch08/ ...三、文章大綱
Section titled “三、文章大綱”Part 0 — 心智模型與工具鏈(2 篇)
Section titled “Part 0 — 心智模型與工具鏈(2 篇)”01. 為什麼是 Workers:isolate 不是「更快的 Lambda」
Section titled “01. 為什麼是 Workers:isolate 不是「更快的 Lambda」”- 學習目標:能說清楚 V8 isolate 與 container-based serverless 在冷啟動、記憶體、並行、計費上的根本差異,並判斷哪些工作負載不適合放上去。
- 涵蓋:V8 isolate vs container/microVM;為什麼沒有傳統 cold start;每個 request 的 CPU time 而非 wall time 計費;
nodejs_compat的邊界(不是 Node.js,是實作了部分 Node API 的獨立 runtime);沒有檔案系統、沒有長駐 process、沒有eval();Free 10ms CPU vs Paid 30s CPU 的設計含義。 - 獨立範例:同一支 API 分別部署到 Workers 與一個傳統 Node 容器,量測 p50/p99 與冷啟動。
- LinkForge:定義專案目標與非目標,說明哪些部分故意不放在 edge。
- 陷阱:把 Workers 當 Node.js 用;以為
nodejs_compat等於完整 Node;在全域作用域做 I/O。 - 篇幅:~2,500 字,程式碼少。
02. 工具鏈:Wrangler v4、wrangler.jsonc 與本機開發
Section titled “02. 工具鏈:Wrangler v4、wrangler.jsonc 與本機開發”- 學習目標:建立一個 2026 年標準的 Worker 專案,理解
compatibility_date的真正作用,並知道本機開發到底在跑什麼。 - 涵蓋:
npm create cloudflare@latest;wrangler.jsonc是新專案的建議格式(部分新功能只支援 JSON config),含$schema。compatibility_date/compatibility_flags:為什麼日期會改變 runtime 行為;2026 年的 compat date 預設開啟遠多於 2024 年的 Node API。wrangler types產生worker-configuration.d.ts(取代手動安裝@cloudflare/workers-types)。- 本機開發=真的
workerd(miniflare v4),不是模擬器。 - Remote bindings:per-binding
"remote": true,取代舊的wrangler dev --remote。 - Wrangler v4 起
wrangler kv/d1/r2指令預設操作本機,2024 年的教學指令複製過來會靜默地打錯目標。
- 獨立範例:一個 Worker,
vars+secret+ 一個 remote KV binding,示範本機/遠端混合開發。 - LinkForge:建立 monorepo 骨架、pnpm workspace、共用 tsconfig、
cf-typegenscript。 - 陷阱(不要教):
wrangler publish、node_compat、usage_model、[site]Workers Sites、Service Worker 語法(addEventListener('fetch'))、手動加nodejs_compat_v2。 - 篇幅:~3,000 字。
Part 1 — Workers 核心(5 篇)
Section titled “Part 1 — Workers 核心(5 篇)”03. 執行模型:fetch handler、ctx 與請求生命週期
Section titled “03. 執行模型:fetch handler、ctx 與請求生命週期”- 學習目標:掌握一次 invocation 的完整生命週期,知道什麼時候程式碼會被中止。
- 涵蓋:ES module handler 簽章
{ fetch(request, env, ctx) };ctx.waitUntil()與背景工作;ctx.passThroughOnException();request.cf的地理/TLS 資訊;subrequest 限制(Free 50 / Paid 1000);CPU time vs wall time;全域作用域可以放什麼(唯讀常數、client 建構)、不能放什麼(任何 I/O);import { env } from "cloudflare:workers"的 top-level 存取及其限制。 - 獨立範例:一支 Worker 展示
waitUntil有無的行為差異,用 log 證明背景工作被砍掉。 - LinkForge:
apps/redirector第一版 — 硬編碼 map、302 redirect、waitUntil記錄一筆 log。 - 限制:Free 10ms / Paid 30s CPU(可設定到 5 分鐘)、128MB 記憶體、script size 壓縮後 Free 3MB / Paid 10MB、startup CPU 1s、subrequest Free 50 / Paid 10,000、同時等待 response header 的連線上限 6。
- 篇幅:~2,800 字。
04. Bindings:env 是整個平台的介面
Section titled “04. Bindings:env 是整個平台的介面”- 學習目標:理解 binding 作為 capability 的設計哲學,以及它為什麼比環境變數字串好。
- 涵蓋:binding 是能力而非連線字串(無需 credential、無需網路設定);
varsvssecrets(wrangler secret put)vs Secrets Store;多環境(Wrangler environments,不是已棄用的 Service Environments);型別如何從wrangler.jsonc流到 TS;process.env在文字 binding 上的自動填充。 - 獨立範例:同一支 Worker 在 dev/staging/prod 三組 binding 下的行為。
- LinkForge:定義完整
Env介面與環境切分策略。 - 陷阱:把 secret 寫進
vars;用--env覆蓋時忘記 binding 不會繼承。 - 篇幅:~2,200 字。
05. 用 Hono 打好 API 骨架
Section titled “05. 用 Hono 打好 API 骨架”- 學習目標:建立一套可長期維護的 API 分層,並解決 Hono
Env與 WranglerEnv的型別衝突。 - 涵蓋:
hono@4.x(沒有 v5);new Hono<{ Bindings: CloudflareBindings; Variables: {...} }>()。wrangler types --env-interface CloudflareBindings— 避開 Hono 自己的Env型別被 shadow。- Middleware:
cors、logger、自訂 auth、錯誤處理與HTTPException。 - 驗證:
@hono/zod-validator(或@hono/standard-validator,Standard Schema v1,較有未來性)。 @hono/zod-openapi產生 OpenAPI 文件(注意 v1 起只吃 Zod 4)。- Hono RPC:
hc<AppType>的硬性條件(兩端同版本、strict: true、路由必須鏈式定義、務必明寫 status)。
- 獨立範例:一支 CRUD API,含 zod 驗證、錯誤處理、OpenAPI 與型別安全 client。
- LinkForge:
apps/api的路由分層、middleware stack、統一錯誤格式。 - 陷阱:
hono/cloudflare-workers的serveStatic已棄用(綁在 Workers Sites 上),改用assets設定。 - 篇幅:~3,500 字。
06. Cache API 與 HTTP 快取:edge 最被低估的一層
Section titled “06. Cache API 與 HTTP 快取:edge 最被低估的一層”- 學習目標:在打任何資料庫之前先想快取,並理解 Cloudflare 三層快取的差異。
- 涵蓋:
caches.defaultvscaches.open();cache key 設計(含Vary陷阱);Cache-Control/s-maxage/stale-while-revalidate;Cache Rules 與 Tiered Cache;cf.cacheEverything/cf.cacheTtlByStatus於fetch()的用法;快取失效策略(purge API vs 版本化 key);為什麼 Cache API 常比 KV 快也比 KV 便宜。 - 獨立範例:同一個昂貴 upstream 查詢,比較「無快取 / Cache API / KV」的延遲與成本。
- LinkForge:redirect 熱路徑加上 Cache API 前置層。
- 陷阱:Cache API 是每個 colo 獨立、不保證留存;把它當持久儲存。
- 篇幅:~2,800 字。
07. Static Assets 與全端 Worker(以及 Pages 的現況)
Section titled “07. Static Assets 與全端 Worker(以及 Pages 的現況)”- 學習目標:能正確設定靜態資源,並對 Pages vs Workers 給出有依據的建議。
- 涵蓋:
assets的五個欄位:directory、binding、html_handling、not_found_handling、run_worker_first。not_found_handling預設是"none"— SPA 必須明確設成"single-page-application",這是最常見的白畫面來源。run_worker_first: ["/api/*", "!/api/docs/*"]的路由陣列形式。env.ASSETS.fetch()只有這一個方法。_headers(上限 100 條)/_redirects(上限 2,100 條)在 Workers 上的支援,以及它們不作用於 Worker 產生的 response。- Pages 的正確表述:官方說法是「新專案請用 Workers」、「所有投資都在 Workers」,但 Pages 並未宣告 deprecated、也沒有 maintenance mode banner。誠實說明剩餘差距:file-based routing(
functions/)、Pages Plugins、branch alias、非 Cloudflare zone 的自訂網域。
- 獨立範例:一個 Worker 同時服務 SPA 與
/api/*。 - LinkForge:把 dashboard build 產物掛上去。
- 陷阱:宣稱 Pages 已死(會失去讀者信任);
experimental_serve_directly(已移除)。 - 篇幅:~2,600 字。
Part 2 — 資料層(6 篇)
Section titled “Part 2 — 資料層(6 篇)”08. Workers KV:最終一致性要怎麼用才對
Section titled “08. Workers KV:最終一致性要怎麼用才對”- 學習目標:把 KV 用在它擅長的地方,並能說出三個絕對不該用 KV 的情境。
- 涵蓋:
get(key, "text"|"json"|"arrayBuffer"|"stream"),效能由快到慢:stream → arrayBuffer → text → json。- Bulk read:
get(keys[], type?)回傳Map,最多 100 key、只支援 text/json、只計 1 次 operation。 getWithMetadata、put的expirationTtl(最小 60 秒)與 metadata(≤1024 bytes)。list()必須看list_complete而非keys.length。cacheTtl最小值 2026-01-30 起由 60s 降為 30s,預設仍是 60s。- 一致性:全球傳播「最多 60 秒或更久」,read-your-own-write 不保證。負向查詢(404)也會被快取、也會計費。
- 每個 key 每秒 1 次寫入上限 —— 這條規定直接判死計數器、sliding session、last-seen 等模式。
- 獨立範例:feature flag 服務,示範傳播延遲的實測。
- LinkForge:redirect 熱路徑改用 KV;設計
link:{slug}key 與 metadata(放 tenant id + 狀態,省一次 D1 查詢)。 - 限制/計費:Free 10 萬讀/日、1000 寫/日、1GB;Paid 每百萬讀 $0.50、每百萬寫/刪/list $5.00。key 512B、value 25MiB、單次 invocation 上限 1000 次 KV 操作。
- 陷阱:把 KV 當強一致資料庫、當計數器、當 queue;用 binding 做 bulk write(不支援,只能走 REST/Wrangler)。
- ⚠️ 時效:舊版 REST 路徑
/accounts/{id}/workers/namespaces/*2026-10-15 停用,改用/accounts/{id}/storage/kv/namespaces/*。 - 篇幅:~3,200 字。
09. D1 入門:邊緣上的 SQLite
Section titled “09. D1 入門:邊緣上的 SQLite”- 學習目標:建立 D1 資料庫、跑 migration、寫出符合 D1 計費模型的查詢。
- 涵蓋:D1 = SQLite + 全球讀取複本;
prepare().bind().run()/.first()/.all()/.raw();batch()是隱式 transaction,單次網路往返;migrations_dir與wrangler d1 migrations create/apply(含--local/--remote);meta欄位(rows_read、rows_written、duration、served_by)——rows_read是計費單位,index 設計直接等於帳單;Time Travel(Free 7 天 / Paid 30 天)。 - 獨立範例:一個 todo API,刻意示範缺 index 時
rows_read如何爆炸。 - LinkForge:
tenants/users/links/click_rollupschema 第一版與初始 migration。 - 限制:Free 10 個 DB、單庫 500MB、500 萬讀列/日;Paid 5 萬個 DB、單庫 10GB、每次 invocation 1000 次查詢;查詢逾時 30s、單一 statement 100KB、綁定參數 100 個、單列 2MB。
- 陷阱:用
exec()跑應用查詢(官方明確不建議,效能與安全都差);傳undefined進bind()(D1_TYPE_ERROR);期待 BigInt;以為 boolean 有原生型別。 - 篇幅:~3,200 字。
10. D1 + Drizzle ORM:型別安全的資料層
Section titled “10. D1 + Drizzle ORM:型別安全的資料層”- 學習目標:建立可維護的 schema-first 工作流,並避開目前版本線的地雷。
- 涵蓋:
- 版本抉擇(本篇最重要的一段):
drizzle-ormnpmlatest是 0.45.x,但 orm.drizzle.team 官網預設文件是 v1.0.0-rc。兩條線 API 不同,必須先選定。 - v0 → v1 的破壞性變更:SQLite driver 的
schema選項被Omit掉,改用relations;relations()→defineRelations();casing選項移除;drizzle-zod等併入主套件;getTableColumns()→getColumns()。 - migration 佈局不相容:drizzle-kit 0.31.x 輸出
0000_x.sql+meta/_journal.json(wrangler d1 migrations apply吃得到);drizzle-kit 1.0-rc 改成out/<ts>_<name>/migration.sql資料夾式,wrangler 找不到。本篇給出兩條線各自的可行工作流。 driver: 'd1-http'支援migrate/push/pull/studio。db.transaction()在 D1 上型別過得了、執行必炸(D1 拒絕裸BEGIN/SAVEPOINT)。一律用batch()。
- 版本抉擇(本篇最重要的一段):
- 獨立範例:完整 schema → migration → seed → query 的一輪。
- LinkForge:
packages/db落地,含 relational query 與batch()寫入。 - 陷阱:以為 ORM 幫你擋掉 transaction 問題;migration 目錄結構與 wrangler 不對齊。
- 篇幅:~3,600 字(本系列最容易踩雷的一篇)。
11. D1 進階:Read Replication 與 Sessions API
Section titled “11. D1 進階:Read Replication 與 Sessions API”- 學習目標:正確啟用全球讀取複本,理解 bookmark 的一致性語意。
- 涵蓋:
- 讀取複本目前仍是 beta;在 dashboard 或 REST 開啟(
read_replication.mode = "auto"),關閉需最多 24 小時。 - 六個區域:ENAM / WNAM / WEUR / EEUR / APAC / OC。
- 最關鍵的一句話:不呼叫
withSession(),所有查詢仍然打 primary,複本等於沒開。 - 三種 constraint:
first-unconstrained(預設,最低延遲)、first-primary(最新資料)、<bookmark>(sequential consistency)。 - 慣用模式:讀取
x-d1-bookmarkheader →withSession(bookmark ?? "first-unconstrained")→session.getBookmark()→ 寫回 response header。 - Sessions API 只能透過 Worker binding,REST API 不支援。
- Drizzle 相容性:v1 RC 的
AnyD1Database已含D1DatabaseSession;0.45.x 需要 cast(runtime 可用)。
- 讀取複本目前仍是 beta;在 dashboard 或 REST 開啟(
- 獨立範例:一個 read-after-write 場景,示範不用 bookmark 時使用者看到舊資料。
- LinkForge:dashboard 讀取路徑套用 session + bookmark。
- 篇幅:~2,800 字。
12. R2:零 egress 費用的物件儲存
Section titled “12. R2:零 egress 費用的物件儲存”- 學習目標:正確使用 R2 的 API 與成本模型。
- 涵蓋:
get/put/head/delete/list;put是強一致;conditional request 的慣用寫法 —— 直接把 request headers 丟進去:onlyIf: request.headers、range: request.headers;multipart upload(單 part 5GiB、最多 10,000 parts、未完成 7 天自動 abort);writeHttpMetadata();storage class(Standard / Infrequent Access);event notifications → Queues(GA);presigned URL 與自訂網域。 - 獨立範例:可續傳的大檔上傳 + Range request 串流下載。
- LinkForge:CSV 匯出、QR code、OG image 存放。
- 計費陷阱(本篇核心):egress 永遠免費,但
ListObjects屬於 Class A($4.50/百萬),Get/Head是 Class B($0.36/百萬),delete 免費。一個天真的「列出所有物件再逐一處理」迴圈就是帳單殺手。 - 陷阱:production 使用
r2.dev公開網址(官方明講「不供 production 使用」,速率不定);R2 Data Catalog / R2 SQL 目前仍是 beta,不要當 GA 教。 - 篇幅:~3,000 字。
13. 儲存選型:KV / D1 / R2 / DO storage / Hyperdrive 決策樹
Section titled “13. 儲存選型:KV / D1 / R2 / DO storage / Hyperdrive 決策樹”- 學習目標:面對一個新需求,能在五分鐘內選對儲存並說出理由。
- 涵蓋:一致性模型對照(eventual / strong / serializable);讀寫比例;單 key 熱點;資料大小;查詢複雜度;成本維度差異(KV 算 operation、D1 算 rows read、R2 算 class A/B、DO 算 GB-s + 儲存)。以 LinkForge 的六個實際需求逐一走過決策。
- 產出:一張決策流程圖 + 一張成本維度對照表(本篇是全系列最常被回頭查的一篇)。
- 篇幅:~2,400 字,圖表為主。
Part 3 — 有狀態運算(5 篇)
Section titled “Part 3 — 有狀態運算(5 篇)”14. Durable Objects:邊緣上的 actor
Section titled “14. Durable Objects:邊緣上的 actor”- 學習目標:認出「只有 DO 能解」的問題形狀,並寫出第一個 DO。
- 涵蓋:
- 每個 DO 全球唯一、單執行緒、有身分 —— 這三點合起來解決的是**協調(coordination)**問題,不是儲存問題。
- 2026 年的正確寫法:
import { DurableObject } from "cloudflare:workers",class X extends DurableObject,constructor 是(ctx, env)(官方文件註明:舊稱state,現在叫ctx)。 - Addressing:優先用
env.MY_DO.getByName("foo"),而非idFromName()+get()。 - RPC method 優先於
fetch()—— 非 HTTP 語意的互動不該包成 Request。 - Migration:
new_sqlite_classes是新專案的唯一選擇;new_classes(KV-backed)是 legacy,且新帳號已無法建立。 blockConcurrencyWhile()的正確用途(初始化)與誤用(當鎖用)。- Location hints(含 2026 新增的
apac-ne/apac-se)。
- 獨立範例:一個全域唯一的 rate limiter。
- LinkForge:
LinkCounterDO —— 解決「KV 每秒 1 寫」擋掉的即時計數需求。 - 陷阱:DO class 沒有 extends
DurableObject;用new_classes;把 DO 當一般資料庫(單物件約 1,000 req/s 軟上限)。 - 篇幅:~3,400 字。
15. DO SQLite Storage:每個 object 一顆資料庫
Section titled “15. DO SQLite Storage:每個 object 一顆資料庫”- 學習目標:使用 SQLite-backed storage,理解它與 D1 的定位差異。
- 涵蓋:
ctx.storage.sql.exec(query, ...bindings)回傳SqlStorageCursor(.toArray()、.one()、.raw()、columnNames、rowsRead、rowsWritten);同步 KV APIctx.storage.kv.*(僅 SQLite-backed);transactionSync()—— DO 內部有真正的 transaction,這是它相對 D1 的關鍵優勢;ctx.storage.sync();PITR:getCurrentBookmark()/getBookmarkForTime()/onNextSessionRestoreBookmark(),30 天視窗;每個 object 上限 10GB。 - 獨立範例:一個協作文件的 per-document DO,含真 transaction 與 PITR 還原。
- LinkForge:
LinkCounter用 SQLite 存明細,取代記憶體變數。 - 限制/計費:Free 100k req/日 + 13,000 GB-s/日;Paid 每百萬 req $0.15、每百萬 GB-s $12.50、儲存每 GB-月 $0.20(含 5GB)。hibernate 中的閒置 object 不計 duration。
- 陷阱:把非同步 KV storage API 當主要儲存介面教(那是舊介面);忽略
rowsRead就是帳單。 - 篇幅:~3,000 字。
16. WebSocket Hibernation:讓一萬條連線幾乎不花錢
Section titled “16. WebSocket Hibernation:讓一萬條連線幾乎不花錢”- 學習目標:用 hibernation API 做即時功能,理解它與傳統 WebSocket 寫法的成本差異。
- 涵蓋:
ctx.acceptWebSocket(ws, tags?)+webSocketMessage/webSocketClose/webSocketErrorhandler;getWebSockets(tag?)、getTags(ws);setWebSocketAutoResponse()(ping/pong 完全不喚醒 object);setHibernatableWebSocketEventTimeout();訊息上限 2025-10 起由 1MiB 提升到 32MiB。 - 成本對比(本篇核心):
ws.accept()+addEventListener會讓 object 持續存活、持續累積 GB-s;ctx.acceptWebSocket()讓 object 在閒置時被驅逐、連線由 runtime 保持。同樣 1,000 條閒置連線,帳單差好幾個數量級。 - 獨立範例:多人游標同步房間。
- LinkForge:
LiveDashboardDO —— 即時點擊看板。 - 陷阱:混用兩種 API;在 hibernation 模式下依賴 instance 記憶體狀態(object 會被驅逐,狀態必須落在 storage)。
- 篇幅:~3,200 字。
17. DO Alarms:內建的排程與去抖動
Section titled “17. DO Alarms:內建的排程與去抖動”- 學習目標:用 alarm 取代大部分「需要一個 cron」的直覺。
- 涵蓋:
setAlarm()/getAlarm()/deleteAlarm();alarm(alarmInfo?)handler;at-least-once 語意 + 指數退避重試 —— handler 必須 idempotent;alarm 與 hibernation 的互動;2026-02 起deleteAll()也會刪 alarm。 - 模式:debounce(大量寫入合併成一次落庫)、per-entity 排程(每個使用者自己的到期時間,不需要掃全表的 cron)、TTL 清理。
- 獨立範例:一個把高頻寫入 debounce 成每 10 秒落庫一次的 aggregator。
- LinkForge:
LinkCounter用 alarm 每 30 秒把計數 flush 進 D1 rollup 表。 - 篇幅:~2,600 字。
18. Workers RPC 與 Service Bindings:把單體拆開
Section titled “18. Workers RPC 與 Service Bindings:把單體拆開”- 學習目標:用 RPC 做服務拆分而不付出網路代價。
- 涵蓋:
servicesbinding 的三個欄位:binding、service、entrypoint;也支援remote: true。class X extends WorkerEntrypoint,this.env/this.ctx是 class 屬性。- 最大誤解:每次 invocation 都是新 instance,不能跨 request 保留狀態。
- 可跨邊界傳遞:structured-cloneable(上限 32MiB)、function(變成 callback stub)、
RpcTarget子類實例、stream、Request/Response、stub。不是RpcTarget子類的 class 傳不過去。 - Promise pipelining:省掉中間的
await可以把多趟往返壓成一趟。 - 資源釋放:
using counter = await env.SVC.newCounter()(Wrangler v4 原生支援);Symbol.asyncDispose不支援。 - 保留字:
fetch、connect、dup、constructor。 - fetch 式 service binding 並未棄用,只是 RPC 是建議預設。
- 獨立範例:把一個單體 Worker 拆成 api + auth 兩支,用 RPC 串起來並示範 pipelining 的延遲差異。
- LinkForge:抽出
TenantService,讓 redirector 與 api 共用。 - 篇幅:~3,000 字。
Part 4 — 非同步與事件流(5 篇)
Section titled “Part 4 — 非同步與事件流(5 篇)”19. Queues:把工作推離熱路徑
Section titled “19. Queues:把工作推離熱路徑”- 學習目標:建立可靠的非同步管線,並算得出它的帳單。
- 涵蓋:producer/consumer 設定(
max_batch_size、max_batch_timeout、max_retries、dead_letter_queue、max_concurrency、retry_delay);send()/sendBatch()(contentType預設是"json");delaySeconds(0–86400);consumer 的queue(batch, env, ctx);message.ack()/retry({delaySeconds})/batch.ackAll()/retryAll();attempts從 1 開始;metrics()讀 backlog;pull consumer(HTTP,含visibility_timeout_ms與 lease)。 - 計費模型(必講):一次 operation = 每 64KB 的寫、讀、或刪。一則典型訊息 = 3 次 operation,每次 retry 再加一次讀。Free 每日 10,000 operations;Paid 每百萬 $0.40。
- 獨立範例:webhook fan-out,含毒訊息進 DLQ。
- LinkForge:redirect 熱路徑只做
send(),apps/consumers負責寫 Analytics Engine + D1 rollup。 - 限制:訊息 128KB、
sendBatch100 則 / 256KB、每佇列 5,000 msg/s、backlog 25GB、push consumer 250 併發、consumer wall clock 15 分鐘。Free 保留期固定 24 小時且不可調。 - 陷阱:宣稱 Queues 需要付費方案(已非事實);期待順序保證(只有 best-effort);移除 consumer worker 後忘了清掉
queues.consumers區塊(deploy 會失敗)。 - 篇幅:~3,400 字。
20. Cron Triggers:排程任務
Section titled “20. Cron Triggers:排程任務”- 學習目標:寫出可測試的排程任務,知道什麼時候不該用 cron。
- 涵蓋:
triggers.crons(支援LW之類的擴充語法);scheduled(controller, env, ctx),controller.cron/.scheduledTime/.noRetry();本機測試的正確方式:純wrangler dev+curl "http://localhost:8787/cdn-cgi/handler/scheduled?cron=*+*+*+*+*&time=..."。 - ⚠️ 不要教:
wrangler dev --test-scheduled與/__scheduled—— 兩者都已從現行文件消失。 - 限制:cron 是帳號層級配額,Free 5 / Paid 250;CPU Free 10ms、Paid 30s(<1 小時間隔)/ 15 分鐘(≥1 小時間隔);wall clock 一律 15 分鐘;變更最多 15 分鐘生效。
- 何時不該用 cron:需要 per-entity 排程時用 DO alarm;需要長時間多步驟時用 Workflows。
- LinkForge:每日清理過期連結。
- 篇幅:~2,000 字。
21. Workflows:durable execution
Section titled “21. Workflows:durable execution”- 學習目標:把多步驟、跨小時甚至跨月的流程寫成看起來像同步程式碼的東西。
- 涵蓋:
class X extends WorkflowEntrypoint<Env, Params>,run(event, step);step.do(name, config?, cb)的重試設定;step.sleep/sleepUntil(不計入 step 上限);step.waitForEvent(name, {type, timeout})(預設 24 小時,逾時會 throw);instance API:create/createBatch/get→status/pause/resume/terminate/restart/sendEvent;2026-06 新增schedules設定(Workflow 自帶 cron);step 可回傳ReadableStream<Uint8Array>以突破 1MiB 結果上限。 - 關鍵教學點:
waiting狀態的 instance 不計入併發配額 —— 可以同時有數百萬個 instance 在睡覺。 - Workflows vs Queues vs DO alarm 的分工表。
- 限制:steps Free 1,024 / Paid 10,000(可申請 25,000);併發 Free 100 / Paid 50,000;step 結果 1MiB;保留 Free 3 天 / Paid 30 天。
- 獨立範例:訂單處理流程,含人工審核(
waitForEvent)與補償步驟。 - LinkForge:每週報表 Workflow —— 聚合 → 產 CSV 進 R2 → 寄信。
- 註記:Dynamic Workflows(2026-05)是建在 Dynamic Workers 上的開源函式庫(
@cloudflare/dynamic-workflows,open beta),不是 Workflows v2、也不是它的替代品,留到 Part 6 再談。 - 篇幅:~3,400 字。
22. Analytics Engine:自己蓋分析後台
Section titled “22. Analytics Engine:自己蓋分析後台”-
學習目標:寫入高基數時序資料並寫出正確的聚合查詢。
-
涵蓋:
writeDataPoint({ blobs, doubles, indexes })—— 非阻塞、不需await;blobs是字串維度(最多 20,總計 ≤16KB)、doubles是數值(最多 20)、indexes只能有一個(sampling key,≤96 bytes);SQL API(ClickHouse 方言)走POST /accounts/{id}/analytics_engine/sql。 -
Sampling(本篇的靈魂):寫入端與查詢端都會抽樣,
_sample_interval是每列各自的抽樣倒數。想算 錯 對 計數 count()sum(_sample_interval)加總 sum(bytes)sum(bytes * _sample_interval)平均 avg(bytes)sum(bytes*_sample_interval)/sum(_sample_interval)分位數 quantile(0.5)(bytes)quantileExactWeighted(0.5)(bytes,_sample_interval) -
限制:每次 invocation 250 個 data point、保留 3 個月;Free 每日 10 萬寫入 / 1 萬查詢。目前尚未開始計費(官方仍寫「未來數月才開始」)—— 要標註查證日期。
-
獨立範例:API latency 儀表板 + Grafana 接入。
-
LinkForge:完整點擊分析後台(來源國家、referrer、UA、時間序列)。
-
篇幅:~3,000 字。
23. Pipelines 與 R2 SQL:把事件落地成資料湖(beta)
Section titled “23. Pipelines 與 R2 SQL:把事件落地成資料湖(beta)”- 學習目標:了解 Cloudflare Data Platform 的三件式模型,判斷現在該不該用。
- 涵蓋:Streams / Pipelines / Sinks 三物件模型(2025-09 重構,建在 Arroyo 之上);HTTP ingest 或 Worker binding;SQL transform(stateless only,且建立後不可修改,只能刪除重建);sink 到 R2(JSON/Parquet)或 R2 Data Catalog(Apache Iceberg),exactly-once;用
wrangler r2 sql query讀回來。 - ⚠️ binding 鍵名已更名:現在是
{ "binding": "X", "stream": "<STREAM_ID>" };舊的"pipeline"仍可用但會警告,且官方部分文件頁仍是舊的。 - 狀態:open beta、僅 Workers Paid。計費訊號目前官方頁面互相矛盾(changelog 說尚未開始計費,pricing 頁列出費率),文章中要明講並要求讀者自行確認。
- 獨立範例:從 HTTP ingest 到 Iceberg 表的完整一輪。
- LinkForge:點擊事件的長期歸檔(標為 optional,因為需要付費方案)。
- 篇幅:~2,600 字,明確標示 beta。
Part 5 — 全端與前端(4 篇)
Section titled “Part 5 — 全端與前端(4 篇)”24. React Router v8 on Workers
Section titled “24. React Router v8 on Workers”⚠️ 本篇是全系列時效性最強的一篇。 React Router v8.0(2026-06 GA)移除了
AppLoadContext,2025 年幾乎所有教學(包含 Cloudflare 官方 RR 指南與官方 starter template)的 binding 存取寫法在 v8 上會直接 throw。
- 學習目標:用 v8 的正確方式部署 SSR 全端應用。
- 涵蓋:
- 新的 binding 存取方式(C3 實際產出的寫法):loader 直接
import { env } from "cloudflare:workers",完全不用 load context。 - 需要顯式 context 時:
createContext()+RouterContextProvider(createRequestHandler現在會硬性拒絕普通物件)。 vite.config.ts:cloudflare({ viteEnvironment: { name: "ssr" } })+reactRouter()。- 通常不用寫
assets區塊 ——vite build會自動產出含assets.directory的wrangler.json。 - v8 baseline:Node ≥22.22、React ≥19.2、Vite 7/8(Vite 6 不再相容);middleware 已是預設功能。
- 已移除:
cloudflareDevProxy()、@react-router/dev/vite/cloudflare、react-router-dom。 - SPA mode 與 prerendering 不支援 Cloudflare Vite plugin(官方明載)。
- 新的 binding 存取方式(C3 實際產出的寫法):loader 直接
- 獨立範例:一個含 loader / action / middleware 的 SSR app,直接讀 D1。
- LinkForge:
apps/dashboard落地,搭配 TanStack Query 做 client 端快取。 - 篇幅:~3,600 字。
25. Astro on Workers
Section titled “25. Astro on Workers”⚠️ 同樣有大版本斷層:Astro 7 +
@astrojs/cloudflarev14。Astro.locals.runtime.*在 adapter v13 已移除、v14 連警告都拿掉,現在直接失敗。
- 學習目標:用 adapter v14 部署內容站與混合 SSR。
- 涵蓋:
- 設定已成 zero-config:
adapter: cloudflare()。 - 新的存取方式:
import { env } from 'cloudflare:workers';Astro.request.cf;caches.default;Astro.locals.cfContext.waitUntil()。 - 已移除:
platformProxy、routes/_routes.json、cloudflareModules、workerEntryPoint、泛型Runtime<Env>型別。 - Sessions 自動設定:adapter 自動注入 KV driver,wrangler 部署時自動 provision namespace,
Astro.session?.get()直接可用。 - Images:Sharp 是原生 libvips addon,在 workerd 跑不起來(v13 起連
astro dev都是 workerd),預設已改為imageService: 'cloudflare-binding'。 - Adapter v13 起完全不再支援 Pages。
- 多環境部署改為
CLOUDFLARE_ENV=staging astro build && wrangler deploy(plugin 在 build 時解析環境)。
- 設定已成 zero-config:
- ⚠️ 不要用
npm create cloudflare -- --framework=astro—— C3 目前產出的 template 仍然塞入已不存在的platformProxy與失效的Runtime<Env>。改用npm create astro@latest+npx astro add cloudflare。 - 獨立範例:Content Collections 內容站 + 一條 SSR API route。
- LinkForge:
apps/site行銷頁與文件。 - 篇幅:~3,000 字。
26. 前後端整合:型別共享與 monorepo
Section titled “26. 前後端整合:型別共享與 monorepo”- 學習目標:讓一份型別從 D1 schema 一路流到 React component。
- 涵蓋:Drizzle schema → zod → Hono route →
hc<AppType>→ TanStack Query;pnpm workspace 與@repo/*內部套件;@cloudflare/vite-plugin的auxiliaryWorkers(一個指令同時跑多個 Worker,service binding 與 RPC 在本機就能通);devOnlyworker;輸出目錄是dist/<vite-environment-name>(不是 worker 名稱)。 - 獨立範例:兩個 Worker + 一個前端,本機一鍵啟動。
- LinkForge:完整 monorepo dev 體驗定案。
- 篇幅:~2,800 字。
27. Auth on the Edge
Section titled “27. Auth on the Edge”- 學習目標:在沒有長駐 process、CPU 受限的環境下做對認證。
- 涵蓋:
- Session 儲存選型:KV(Cloudflare 官方建議,但 read-your-own-write 不保證 → 登出/撤銷有漏洞窗口;每 key 每秒 1 寫 → sliding expiration 做不了)vs DO(立即撤銷、單次性 token、rate limit)vs D1(要 join user 資料時)。
- 密碼雜湊的 CPU 陷阱(本篇最實用的一段):
node:cryptoscrypt(需nodejs_compat)—— 目前最佳解,N*r*p ≤ 1,048,576。- PBKDF2 在 production 被硬限制在 100,000 次迭代(OWASP 建議 600,000);更糟的是 workerd 本機版沒有這個限制 —— 600k 在
wrangler dev會過、部署後炸NotSupportedError。 - bcrypt/argon2 原生模組、
WebAssembly.compile()皆不可用。 - Free plan 10ms CPU 根本跑不完 scrypt,密碼登入實質上需要 Paid。
- 函式庫現況:Better Auth 可用但 Cloudflare 官方零支援(無 guide、無 template、
@better-auth/cloudflare不存在;社群套件是better-auth-cloudflare),且必須 per-request 建構auth實例;Auth.js(@auth/core+@auth/d1-adapter)是最乾淨的路徑、core 甚至不需要nodejs_compat;Lucia 已棄用、OpenAuth 已停更約 16 個月(不要開新專案用)。 session.cookieCache是 Workers 上投報率最高的一個開關 —— 省掉每個已登入請求的一次 D1/KV 往返。- JWT:
jose@6(零依賴、純 WebCrypto、不需任何 flag)、hono/jwt的verifyWithJwks();Cloudflare Access 要驗Cf-Access-Jwt-Assertionheader(不是 cookie),且一定要驗aud,否則同 team 任一 app 的 token 都會被接受。 - Workers 專屬 crypto:
crypto.subtle.timingSafeEqual()、new crypto.DigestStream()、原生 Ed25519。
- 獨立範例:email + password 登入(DO session store)與 OAuth 登入(Auth.js)兩版對照。
- LinkForge:多租戶 auth、role、API token。
- 篇幅:~4,000 字(全系列最長)。
Part 6 — 進階平台能力(6 篇)
Section titled “Part 6 — 進階平台能力(6 篇)”28. Hyperdrive:接上你既有的 Postgres / MySQL
Section titled “28. Hyperdrive:接上你既有的 Postgres / MySQL”- 學習目標:讓 Workers 能用既有關聯式資料庫,並避開快取一致性陷阱。
- 涵蓋:
hyperdrivebinding + 必要的nodejs_compat;Postgres 用pg≥8.13 或postgres.js,new Client({ connectionString: env.HYPERDRIVE.connectionString });MySQL 必須用離散欄位而非 connectionString,且mysql2必須加disableEval: true(Workers 沒有eval())—— 這是 MySQL 使用者第一大坑;localConnectionString讓本機 dev 也能連。 - ⚠️ 最重要的正確性陷阱:query cache 不會因為寫入而失效(
max_age預設 60s,stale_while_revalidate15s)。read-after-write 路徑必須用第二組--caching-disabled設定。 - 限制:Free 10 / Paid 25 個設定;連線數 Free ~20 / Paid ~100;查詢最長 60s;快取回應上限 50MB。
- 獨立範例:既有 Postgres 上的 API,示範快取命中率與寫後讀錯誤。
- LinkForge:模擬「從既有系統遷移」的章節(optional path)。
- 篇幅:~2,800 字。
29. Containers on Workers(GA 2026-04)
Section titled “29. Containers on Workers(GA 2026-04)”- 學習目標:知道什麼工作該掉出 isolate,並正確設定容器生命週期。
- 涵蓋:
- 容器一定由一個 Durable Object 當前門 ——
containers[].class_name必須與durable_objects.bindings[].class_name一致,而env上出現的是 DO binding 的name。這是最多人設定錯的地方。 - migration 必須用
new_sqlite_classes。 Containerclass(@cloudflare/containers):defaultPort、sleepAfter(預設"10m")、envVars、enableInternet;hooksonStart/onStop/onError/onActivityExpired()(必須呼叫stop()或destroy(),否則容器洩漏)。- Instance types:
lite/basic/standard-1…standard-4(舊的dev與裸standard已不存在);上限 4 vCPU / 12GiB。 - 計費:每 10ms 實際執行時間,GA 起改為 active-CPU 計價,閒置 wall-clock 不計。
- 沒有 GPU container(唯一相關資料是 2024 年談 Cloudflare 內部平台的文章,不適用)。
- Sandboxes(
@cloudflare/sandbox)是建在 Containers 上的另一個產品,專供不可信程式碼執行,不要混談。
- 容器一定由一個 Durable Object 當前門 ——
- 獨立範例:用容器跑 ffmpeg 轉檔,由 Worker 排程。
- LinkForge:optional —— 大量 QR/PDF 批次產生。
- 篇幅:~3,000 字(Workers Paid only,明確標註)。
30. Browser Run(原 Browser Rendering)
Section titled “30. Browser Run(原 Browser Rendering)”⚠️ 2026-04-15 更名為 Browser Run,文件路徑改為
/browser-run/。但三件事沒變:REST 路徑仍是.../browser-rendering/...、wrangler 鍵名仍是browser、套件仍是@cloudflare/puppeteer/@cloudflare/playwright。
- 學習目標:產生截圖 / PDF,並且不要燒錢。
- 涵蓋:Quick Actions(
/content、/screenshot、/pdf、/snapshot、/markdown、/scrape、/json、/links,均 GA;/accessibilityTree2026-07 新增;/crawl仍 beta);2026-05 新增的 binding 版env.BROWSER.quickAction("screenshot", { url })(不需 API token,但必須remote: true,本機不支援);Playwright 需 compatibility date ≥ 2025-09-15;session 重用(puppeteer.sessions()/connect())。 - 成本控制(本篇核心):閒置 timeout 60 秒(可延長到 10 分鐘);重用模式結尾要用
browser.disconnect()而非browser.close();官方明指未關閉的 session 是失控帳單第一名。Free 3 併發 / 每日 10 分鐘;Paid 120 併發、含 10 browser-hours/月,之後 $0.09/hr。 - Puppeteer vs Playwright:官方 FAQ 刻意不選邊。可以給出編輯觀點(Playwright 上游版本新得多),但不要宣稱這是 Cloudflare 的建議。
- LinkForge:每個連結自動產生 OG 預覽圖,存進 R2。
- 篇幅:~2,600 字。
31. Images 與媒體交付
Section titled “31. Images 與媒體交付”- 學習目標:處理使用者上傳的圖片,理解 2026 年的計費模型變動。
- 涵蓋:
imagesbinding 的兩個介面 —— (a) 轉換:env.IMAGES.input(stream).transform({width}).output({format}),加上.draw()與.info();(b) 2026-06 新增的 hosted 管理介面:env.IMAGES.hosted.upload()/.list()/.image(id).*(Worker 裡不再需要 API token,但需付費 Images 方案)。fetch()的cf.image並未棄用,與 binding 分工不同(URL 來源 vs 位元組管線)。 - ⚠️ 文件 IA 已重組:
/images/transform-images/*→/images/optimization/*;「Image Resizing」一詞已退役,現在叫 transformations。 - 計費變動:2026-07-01 起,Images binding 改為按 unique transformation 計費(與 URL transform 同模型),而非按呼叫次數;
.info()不再計費。 - 陷阱:把 resize Worker 掛在
/*而不區分原圖/縮圖路徑 → 無窮迴圈;把 resize 選項放進cacheKey;把 Polish 與 Transformations 混為一談。 - LinkForge:使用者頭像與自訂 OG 圖處理。
- 篇幅:~2,400 字。
32. Email:收信路由與寄信
Section titled “32. Email:收信路由與寄信”Cloudflare 已把兩者收攏到新的 Cloudflare Email Service 傘下(
/email-service/),文件正在從/email-routing/搬遷。
- 學習目標:讓 Worker 能收信也能寄信。
- 涵蓋:
- Email Routing(GA,Free 可用) vs Email Sending(2026-04 起 public beta,僅 Workers Paid)。
- 舊規則已被推翻:以前只能寄給已驗證的目的地;現在網域 onboard 完成後即可寄給任意收件人,SPF/DKIM/DMARC 自動設定。
- 現代寄信 API:
{ "send_email": [{ "name": "EMAIL" }] }+await env.EMAIL.send({ to, from, subject, text, html, replyTo })。可選限制allowed_destination_addresses/allowed_sender_addresses。 - legacy:
import { EmailMessage } from "cloudflare:email"+ mimetext 現已被官方標為「legacy API」。 - 收信:
async email(message, env, ctx),setReject()/forward()/reply()(reply 不需要send_emailbinding,但需要有效 DMARC)。 - 本機測試:
/cdn-cgi/handler/email。
- LinkForge:週報寄送、team 邀請信、以及一個
support@收信 → 自動建 ticket 的流程。 - 篇幅:~2,400 字。
33. 多租戶執行使用者程式碼:Workers for Platforms vs Dynamic Workers
Section titled “33. 多租戶執行使用者程式碼:Workers for Platforms vs Dynamic Workers”2026 年最容易搞混的一對產品。本篇的主要價值就是把它們分乾淨。
- 學習目標:面對「讓客戶跑他們自己的程式碼」需求時選對產品。
- 涵蓋:
- Workers for Platforms(GA,$25/月方案):
dispatch_namespacesbinding;env.DISPATCHER.get(name, {}, { limits: { cpuMs, subRequests }, outbound })。注意subRequests的大寫 R(與多數 Cloudflare 設定慣例不同)。錯誤處理e.message.startsWith("Worker not found")→ 404。最佳實務是所有客戶共用一個 namespace,不是每個客戶一個。 - Dynamic Workers(open beta,Workers Paid):產品化的 Worker Loader。
[[worker_loaders]]binding;env.LOADER.get(id, async () => ({ compatibilityDate, mainModule, modules, globalOutbound }))——get()同步回傳 stub,callback 只在 cold isolate 執行且可能執行多次。globalOutbound: null完全封死對外網路。能力導向:Dynamic Worker 只看得到你放進env的東西;自訂 API 走ctx.exportsloopback。計費:每月含 1,000 個 unique Worker,超出每個每日 $0.002。 - 選擇準則:客戶自己 deploy Worker、你用名字 dispatch → Workers for Platforms;你的 Worker 在 runtime 載入生成的程式碼 → Dynamic Workers。兩者互不取代。
- DO Facets:
this.ctx.facets.get(name, cb)—— 屬於 Dynamic Workers,不是 Durable Objects 的核心功能。每個 facet 有自己隔離的 SQLite,適合 per-tenant / AI 生成的應用。 - Dynamic Workflows(
@cloudflare/dynamic-workflows,MIT,open beta):解決「stock Workflows 要求 workflow class 存在於你部署的程式碼中」的問題,讓引擎幾小時後醒來時能重新進入正確租戶的程式碼。wrapWorkflowBinding()+createDynamicWorkflowEntrypoint(),且DynamicWorkflowBinding必須從 Worker Loader 再匯出(很容易漏)。
- Workers for Platforms(GA,$25/月方案):
- 獨立範例:一個讓使用者提交 JS 片段當作 webhook transformer 的平台。
- LinkForge:optional —— 讓租戶自訂 redirect 前置邏輯。
- 陷阱:把 Dynamic Workers 說成 GA;把 Dynamic Workflows 說成「Workflows v2」;說 Workers for Platforms 被取代了。
- 篇幅:~3,200 字。
Part 7 — AI(4 篇)
Section titled “Part 7 — AI(4 篇)”34. Workers AI
Section titled “34. Workers AI”- 學習目標:在 Worker 裡跑推論,並選對模型。
- 涵蓋:
{ "ai": { "binding": "AI" } }(binding 形狀是 2025 年唯一能原封不動沿用的東西);env.AI.run(model, inputs, options)——stream: true放在第二個參數(inputs),不是第三個;AiOptions:gateway、websocket、returnRawResponse、extraHeaders、signal;env.AI.toMarkdown();function calling;JSON mode;prompt caching(extraHeaders: {"x-session-affinity": "ses_..."});2026-03 重新設計的 pull-based batch({queueRequest: true}→ 以request_id輪詢)。 - ⚠️ 2026 最大變化:
env.AI.run()不再只跑 Workers AI。2026-04 起可呼叫第三方模型(await env.AI.run("openai/gpt-4.1-mini", {...}, { gateway: { id: "default" } }),第三方必須帶 gateway)。要把三個概念分清楚:@cf/...Workers AI(Neurons 計費)/@cf/deepgram/...夥伴模型(也是 Neurons)/openai/...第三方(AI Gateway Unified Billing,credits + 5% 手續費,不需自備 API key)。 - 🔴 模型清單陷阱(務必寫進文章):2026-05-30 一次下架 18 個模型,包括
@cf/meta/llama-3.1-8b-instruct、llama-3-8b-instruct、全部llama-2-7b-chat-*、@cf/mistral/mistral-7b-instruct-v0.1、@cf/google/gemma-3-12b-it、@cf/microsoft/phi-2。Cloudflare 自家 quickstart、AI SDK 頁面、JSON mode 支援清單目前都還印著已死的 model ID。不要照抄官方範例。 - 目前的旗艦 model ID:
@cf/moonshotai/kimi-k2.7-code、@cf/zai-org/glm-4.7-flash、@cf/openai/gpt-oss-120b(注意它吃instructions+input,不是messages)、@cf/meta/llama-4-scout-17b-16e-instruct;embedding@cf/google/embeddinggemma-300m。 - 計費:Neurons,每日 10,000 免費,超出 Paid 每千 $0.011。錯誤 3036 = 免費額度用盡;3040 = 容量不足,這個要教重試。
- LinkForge:連結目標的惡意內容掃描 + 自動摘要。
- 篇幅:~3,000 字。
35. AI Gateway
Section titled “35. AI Gateway”- 學習目標:把 LLM 呼叫的快取、限流、觀測、成本統一管起來。
- 涵蓋:
- 入口已經換位置:現在建議走
api.cloudflare.com/client/v4/accounts/{id}/ai/v1/chat/completions(以及/responses、/messages)。 - 兩段式棄用鏈(兩端都已查證):Universal Endpoint 頁面標題直接寫「(Deprecated)」,指向 OpenAI-compatible endpoint;而那一頁自己又寫「本 endpoint 已棄用,請用 REST API」。Provider-specific endpoints 並未棄用。
- 2026-03 起,第一次帶認證的請求會自動建立名為
default的 gateway,不必先手動建。 - 沒有獨立的 AI Gateway binding —— 用同一個
aibinding:env.AI.gateway("name").patchLog() / .getLog() / .getUrl()。(gateway.run()已從文件消失。) - GA 功能:analytics、logging、caching、rate limiting、retries/timeouts、23 家 provider、custom cost、OTel export、Logpush。Beta:spend limit、Dynamic Routing、DLP、Guardrails、BYOK、custom provider、WebSockets。
- Header:
cf-aig-cache-key/-cache-ttl/-skip-cache/-metadata(上限 5)/-max-attempts(上限 5)/-backoff/-request-timeout。已棄用:cf-cache-ttl、cf-skip-cache。 - 認證陷阱:在
gateway.ai.cloudflare.com上,Cloudflare token 放cf-aig-authorization(Authorization留給 provider);在api.cloudflare.com上則是標準Authorization: Bearer。 - Guardrails 用
@cf/meta/llama-guard-3-8b,增加約 500ms,且不支援 streaming。 - AI Gateway 裡沒有 MCP 功能 —— MCP Server Portals 是 Cloudflare One / Zero Trust 的功能,名稱撞車害很多人繞路。
- 入口已經換位置:現在建議走
- LinkForge:所有 AI 呼叫統一走 gateway,接上成本儀表板。
- 篇幅:~2,600 字。
36. Vectorize 與 RAG
Section titled “36. Vectorize 與 RAG”- 學習目標:建立可用的語意搜尋,並理解它的一致性語意。
- 涵蓋:
insert/upsert/query/queryById/getByIds/deleteByIds/describe;returnMetadata是字串列舉'none' | 'indexed' | 'all',不是 boolean;topK上限 100,但returnValues: true或returnMetadata: 'all'時降為 50;filter 運算子$eq/$ne/$in/$nin/$lt/$lte/$gt/$gte,filter JSON < 2048 bytes。 - 🔴 兩個最常見的錯:(1) V2 的寫入是非同步的,回傳
mutationId,要幾秒後才可查詢 —— 教學一定要示範這個 eventual consistency,否則讀者的第一個 demo 就會「查不到」;(2) metadata filter 欄位必須在插入向量之前用wrangler vectorize create-metadata-index宣告。 - 限制:每 index 1,000 萬向量、最多 1536 維、metadata 10KiB/向量、每 index 10 個 metadata index。空 index 免費。
- 獨立範例:文件 RAG,含 chunking 策略與 embedding 模型選擇。
- LinkForge:跨租戶的連結語意搜尋(用 namespace 做租戶隔離)。
- 陷阱:V1 index(早已停止建立);
returnMetadata: true。 - 篇幅:~3,000 字。
37. Agents SDK 與 MCP server on Workers
Section titled “37. Agents SDK 與 MCP server on Workers”⚠️ 不要稱 Agents SDK 為 GA —— 文件裡沒有任何 GA/beta 宣告,版本仍是 0.x,minor 版就會破壞相容。
- 學習目標:在 Workers 上建 stateful agent 與 MCP server。
- 涵蓋:
class Agent<Env, State, Props> extends Server<Env, Props>,繼承鏈是DurableObject > Server > Agent—— agent 本質上就是一個 DO,Part 3 的知識直接複用。- State:
initialState、this.setState()、this.sql\…“(同步)。 - 頭號改名陷阱:server 端 hook 叫
onStateChanged(0.4.0 從onStateUpdate改名,兩個都覆寫會 throw),但 client 端useAgent的選項仍叫onStateUpdate。兩個名字都活著,在不同地方。 - Chat 已搬套件:
import { AIChatAgent } from "@cloudflare/ai-chat"、useAgentChatfrom@cloudflare/ai-chat/react。agents/ai-chat-agent、agents/ai-react現在 import 就 throw。 - 設定必需項:
nodejs_compat、DO binding、new_sqlite_classesmigration、以及有靜態資源時的run_worker_first: ["/agents/*"](否則 SPA 會吞掉 agent 路由)。 - 2026 新增:Fibers(durable execution,
runFiber/ctx.stash())、sub-agents(用 DO facets,只有 parent 需要 binding)、agents-as-tools 與 human-in-the-loop(waitForApproval()由 Workflows 支撐,可以等好幾個月)、Agent Skills。 - MCP 三選一決策表:
createMcpHandler()(無狀態,不需 DO,新的預設)/McpAgent(需要 per-session 狀態時)/raw transport(完全控制)。 - 🔴
McpServer必須 per-request 建構 —— 2025 年流行的 module-scope singleton 在 MCP SDK 1.26+ 已是安全問題。 - OAuth:
@cloudflare/workers-oauth-providerv0.8.2,務必升過 0.8.0(修了 cross-client token revocation 與 auth code exchange 缺陷),並設allowPlainPKCE: false(預設是true)。 - Code Mode(
@cloudflare/codemode,beta):把 MCP tools 轉成 TypeScript API 讓 LLM 寫程式呼叫,需要worker_loadersbinding。
- ⚠️ 不要教:
workers-mcp(2025-03 後已死)、mcp-handler(那是 Vercel 的無關套件,撞名)、宣稱支援 sampling / tool annotations /outputSchema(文件皆無)。MCP dev server 預設埠是 8788 不是 8787。 - LinkForge:一個讓 AI 助理管理連結的 MCP server(OAuth 保護)。
- 篇幅:~3,600 字。
Part 8 — 工程實務(5 篇)
Section titled “Part 8 — 工程實務(5 篇)”38. 測試
Section titled “38. 測試”⚠️
@cloudflare/vitest-pool-workersv0.13.0(2026-03)是破壞性重寫。網路上所有 2025 年的設定範例都不能用。
- 學習目標:建立在真 workerd 裡跑的測試套件。
- 涵蓋:
- 新設定形式是 plugin 不是 pool:
import { cloudflareTest } from "@cloudflare/vitest-pool-workers";export default defineConfig({plugins: [cloudflareTest({ wrangler: { configPath: "./wrangler.jsonc" } })],});
- 需要
vitest@^4.1.0;Vitest 2.x / 3.x 完全不支援。 - 遷移對照:
defineWorkersConfig→cloudflareTest();poolOptions.workers.*→ 直接傳選項;isolatedStorage/singleWorker→ 移除(隔離改為 per-test-file);import { env, SELF } from "cloudflare:test"→import { env, exports } from "cloudflare:workers";SELF.fetch()→exports.default.fetch();fetchMock完全移除(改用 MSW 或直接 mockglobalThis.fetch)。套件內附 codemod。 - ⚠️
exports不像舊的SELF那樣暴露 Assets —— 測靜態資源要用startDevWorker()。 cloudflare:test仍提供:createExecutionContext、waitOnExecutionContext、createScheduledController、createMessageBatch、runInDurableObject、runDurableObjectAlarm、applyD1Migrations,以及新的evictDurableObject/abortAllDurableObjects。- Workflow 測試 API(v0.9.0 起):
introspectWorkflowInstance()、disableSleeps()、mockStepResult()、mockEvent()、waitForStatus()—— 不用真的等三天。 - D1 migration 在測試中:
readD1Migrations()(Node 端)+applyD1Migrations()(setup file)。注意 drizzle-kit v1 的資料夾式 migration 佈局在這裡同樣會出問題。 - ⚠️ 整合會自動注入
nodejs_compat、no_nodejs_compat_v2、export_commonjs_default—— 測試行為可能與 production 分歧,要在 wrangler config 明寫。
- 新設定形式是 plugin 不是 pool:
- LinkForge:unit(DO 邏輯)+ integration(HTTP)+ Workflow 三層測試。
- 篇幅:~3,400 字。
39. Observability:logs、traces 與 tail
Section titled “39. Observability:logs、traces 與 tail”- 學習目標:出事的時候找得到原因。
- 涵蓋:
observability完整 schema(官方設定頁只寫了前兩個 key,是過時的):注意"observability": {"enabled": true, "head_sampling_rate": 1,"logs": { "enabled": true, "invocation_logs": true, "persist": true, "destinations": [] },"traces": { "enabled": true, "head_sampling_rate": 0.05, "persist": true, "destinations": [] }}head_sampling_rate在頂層(2025-04 的官方 blog 把它放在logs底下,是錯的)。- Workers Logs(GA):新 Worker 預設開啟。log JSON 物件才會被抽出欄位並建索引,Query Builder 才查得到巢狀欄位。保留 Free 3 天 / Paid 7 天(7 天是硬上限)。
- Traces / OpenTelemetry(open beta,2026-03 起計費):零程式碼自動 instrument —— 所有 outbound
fetch、所有 binding 呼叫(KV/R2/D1/DO/Queues/Browser/Images/Email)、cache、handler 生命週期。2026-05 起 service binding 與 DO 呼叫已支援分散式追蹤。自訂 span:import { tracing } from "cloudflare:workers"→tracing.enterSpan(name, cb)。 - ⚠️ Trace 注意事項:span 名稱與 attribute 名稱官方明說尚未定案;非 I/O 操作可能顯示 0ms(Spectre 緩解);用
$metadata.service而非service.name篩選 Worker。價格在兩個官方頁面互相矛盾($0.60/M vs $0.05/M),文章不要寫死數字。 - Query Builder(GA)只查 logs dataset,不查 traces。
- Tail Workers:
tail_consumers+async tail(events, env, ctx)(events是陣列);按 CPU time 計費而非請求數。 wrangler tail定位:只適合 live debugging —— 最多 10 個併發觀察者、會抽樣、不留存。- Source maps:
"upload_source_maps": true,上限 15MB(gzip),非同步抓取,不影響 CPU/效能。 - Logpush:dataset
workers_trace_events;logs+exceptions共用 16,384 字元額度。
- LinkForge:結構化 log 規範、trace 抽樣策略、錯誤告警。
- 篇幅:~3,200 字。
40. CI/CD 與發佈策略
Section titled “40. CI/CD 與發佈策略”- 學習目標:建立可回滾的部署流程。
- 涵蓋:Workers Builds(Git 整合)vs GitHub Actions(
cloudflare/wrangler-action);versions 與 gradual deployment ——wrangler versions upload/deploy --version-id X@10%,以及漸進式發佈期間的 DO/Queue 相容性注意事項;rollback;preview URL 與 preview alias;secrets 在 CI 的處理(wrangler secret bulk、Secrets Store);wrangler types --check在 CI 驗證型別是否過期;monorepo 的選擇性部署。 - LinkForge:完整 GitHub Actions pipeline(typecheck → test → deploy staging → smoke test → 漸進式 production)。
- 篇幅:~2,800 字。
41. 安全性與多租戶隔離
Section titled “41. 安全性與多租戶隔離”- 學習目標:在多租戶 SaaS 情境下把邊界劃清楚。
- 涵蓋:secrets vs Secrets Store;每個租戶一個 DO 作為隔離邊界;D1 的 row-level 隔離要靠應用層(沒有 RLS);Vectorize namespace 做向量隔離;rate limiting(Rate Limiting binding vs DO vs WAF,三者取捨);Turnstile;CORS 與 CSRF;signed URL(R2、Images、Stream);使用者提供程式碼時的
globalOutbound: null;Zero Trust / Cloudflare Access 保護管理後台;依賴稽核(Workers 沒有 filesystem 但供應鏈風險一樣在)。 - LinkForge:租戶隔離的威脅模型與逐項對策。
- 篇幅:~3,000 字。
42. 成本工程:算得出你的帳單
Section titled “42. 成本工程:算得出你的帳單”- 學習目標:對任一設計估出月成本,並認出常見的燒錢模式。
- 涵蓋:把全系列的計費維度收攏成一張表 —— Workers(請求 + CPU ms)、KV(operation,注意 404 也計費)、D1(rows read,index 即帳單)、R2(Class A 的 List 是陷阱,egress 免費)、DO(GB-s + 儲存,hibernation 省錢)、Queues(每 64KB 一次 operation,一則訊息約 3 次)、Workers AI(Neurons)、Browser Run(browser-hours + 併發)、Containers(active CPU)。
- 五個經典燒錢模式:熱路徑上的
list()、缺 index 的 D1 查詢、非 hibernation 的 WebSocket、忘記 disconnect 的 browser session、無上限的 retry 迴圈。 - 產出:一份 LinkForge 在 1 萬 / 100 萬 / 1 億次點擊/月三個量級的成本試算表,以及「什麼時候該離開 edge」的判準。
- 篇幅:~2,800 字。
Part 9 — 收尾(1 篇)
Section titled “Part 9 — 收尾(1 篇)”43. LinkForge 總回顧與 production checklist
Section titled “43. LinkForge 總回顧與 production checklist”- 完整架構圖與每個決策的回顧(為什麼這裡用 DO 而不是 D1、為什麼那裡用 KV 而不是 Cache API)。
- Production checklist:observability、告警、備份(D1 Time Travel / DO PITR / R2 版本)、rate limit、成本告警、依賴更新策略、compatibility date 的升級節奏。
- 沒有涵蓋的東西與延伸方向:Zaraz、Stream、Snippets、Zero Trust、Terraform provider。
- 篇幅:~2,400 字。
四、統計與排程
Section titled “四、統計與排程”| 項目 | 數字 |
|---|---|
| 總篇數 | 43 |
| 總字數估計 | 約 125,000 字 |
| 獨立範例數 | 40+(examples/chNN/) |
| 貫穿專案 | 1(LinkForge,8 個 app/package) |
| 建議節奏 | 每週 2 篇,約 5 個月 |
建議發佈順序:Part 0–4(前 23 篇)是核心骨幹,必須按序。Part 5–7 可依讀者回饋調整順序。Part 8 雖然排在最後,但建議提前把第 38 篇(測試)與第 39 篇(observability)的精簡版插進 Part 2 之後,避免讀者累積 20 篇沒有測試的程式碼。
五、維護計畫(本系列的最大風險)
Section titled “五、維護計畫(本系列的最大風險)”這份研究過程中最強烈的發現:Cloudflare 自家文件目前在多處自相矛盾或過時 —— AI Gateway 的兩段式棄用鏈、印著已下架 model ID 的 quickstart、缺少 traces 的 observability 設定頁、仍用 context.cloudflare.env 的 React Router 指南、產出無效設定的 C3 Astro template。
因此:
- 每篇文章頂端標示「最後驗證日期 + wrangler 版本 + compatibility date」。
- 所有程式碼範例以「能否實際部署成功」為準,不以官方文件頁面為準。 本系列的 examples repo 應該有 CI 每週跑一次真實部署。
- 維護一份
VERSIONS.md,鎖定:wrangler4.114.x、@cloudflare/vite-plugin1.47.x、@cloudflare/vitest-pool-workers0.18.x、hono4.12.x、react-router8.3.x、astro7.1.x +@astrojs/cloudflare14.1.x、drizzle-orm(明確選定 0.45.x 或 1.0-rc)。 - 維護一份「不要教」清單(見附錄),每季複查一次。
附錄 A:2026 年「不要教」清單
Section titled “附錄 A:2026 年「不要教」清單”Runtime / Wrangler
Section titled “Runtime / Wrangler”Service Worker 語法(addEventListener('fetch'))· wrangler publish · Workers Sites [site] · node_compat · usage_model · getBindingsProxy() · 手動加 nodejs_compat_v2 · 假設 wrangler kv/d1/r2 指令會打 production(v4 起預設本機)· experimental_remote(已改名 remote)
KV 當強一致資料庫 / 計數器 / queue · KV binding 做 bulk write · D1 exec() 跑應用查詢 · D1 dump() · 以為不用 withSession() 也會走 replica · 說 D1 read replication 是 GA · R2 r2.dev 用於 production · 說 R2 Data Catalog / R2 SQL 是 GA · DO new_classes · DO 非同步 KV storage API 當主介面 · DO state(現在叫 ctx)· ws.accept() + addEventListener(擋掉 hibernation)· Drizzle db.transaction() 於 D1 · Drizzle v1 的 schema 選項 / relations() / casing
placement.hint(不存在,實際是 mode / region / host / hostname)· Smart Placement 作用於 RPC 或 named entrypoint(只作用於 fetch)· wrangler dev --test-scheduled 與 /__scheduled · Container instance type dev / 裸 standard · GPU container(不存在)· 說 Dynamic Workers 是 GA · 說 Dynamic Workflows 是「Workflows v2」· 說 Workers for Platforms 被取代 · subrequests(正確是 subRequests)· Snippets 搭配 bindings/secrets · Email「只能寄給已驗證地址」· cloudflare:email + mimetext 當主要寄信路徑 · Pipelines 的 "pipeline" binding key(已改 "stream")· 說 Pages 已 deprecated
2026-05-30 下架的 18 個模型 · 照抄 Cloudflare 自家 quickstart / AI SDK / JSON mode 範例(都印著死掉的 model ID)· stream: true 放第三個參數 · gateway.run() · Universal Endpoint · cf-cache-ttl / cf-skip-cache · 在 gateway.ai.cloudflare.com 把 CF token 放 Authorization · 說 Guardrails 支援 streaming · 把 MCP portal 歸給 AI Gateway · 說 Agents SDK 是 GA · agents/ai-chat-agent / agents/ai-react(import 即 throw)· server 端 onStateUpdate · AI SDK v4/v5 · workers-mcp · module-scope new McpServer() · mcp-handler(Vercel 的套件)
React Router AppLoadContext / context.cloudflare.env(v8 會 throw)· cloudflareDevProxy() · react-router-dom · Astro Astro.locals.runtime.* / Runtime<Env> / platformProxy / routes / cloudflareModules · hono/cloudflare-workers 的 serveStatic · @cloudflare/next-on-pages(→ @opennextjs/cloudflare)· Lucia(已棄用)· OpenAuth(停更約 16 個月)
測試 / 觀測
Section titled “測試 / 觀測”defineWorkersConfig / defineWorkersProject / poolOptions.workers · cloudflare:test 的 env / SELF · fetchMock · isolatedStorage / singleWorker · Vitest 2.x / 3.x · unstable_dev() · 直接用 Miniflare API 當預設 · 用 Query Builder 查 traces · OTel metrics 匯出(不支援)· 把 trace span 名稱當穩定契約 · 寫死 trace 價格(官方頁面互相矛盾)
附錄 B:版本鎖定基準(2026-07-27 查證)
Section titled “附錄 B:版本鎖定基準(2026-07-27 查證)”wrangler 4.114.0@cloudflare/vite-plugin 1.47.0@cloudflare/vitest-pool-workers 0.18.8 (需 vitest ^4.1.0)miniflare 4.20260722.0create-cloudflare 2.70.14 (Astro template 已過時,勿用)hono 4.12.32 (無 v5)@hono/zod-validator 0.9.0react-router / @react-router/* 8.3.0 (需 Node ≥22.22, Vite 7/8)astro 7.1.3@astrojs/cloudflare 14.1.4drizzle-orm 0.45.2 (latest) / 1.0.0-rc.4 (rc) ← 必須先選線drizzle-kit 0.31.10 / 1.0.0-rc.4better-auth 1.6.25@auth/core 0.41.3jose 6.2.4@cloudflare/workers-oauth-provider 0.8.2 (務必 >0.8.0)agents 0.19.0 (0.x,非 GA)附錄 C:待確認事項(發佈前需複查)
Section titled “附錄 C:待確認事項(發佈前需複查)”- Trace 計費:官方兩個頁面分別寫 $0.60/M 與 $0.05/M。
- Pipelines 計費是否已啟動:changelog 與 pricing 頁說法不一致。
- D1 read replication 是否已 GA:文件仍標 beta,未見 GA 公告。
- Workers AI
queueRequest:Batch 頁面有寫,但 workerd 的AiOptions型別裡沒有,TS 使用者可能需要 cast。 - drizzle-orm 1.0 stable 的發佈時間:無公開時程。
- drizzle-kit v1 與
wrangler d1 migrations apply的目錄不相容:此結論來自反編譯已發佈的 CLI,非官方文件,發佈前務必用自己的out/實測。