第一個 Worker:從建立到部署上線 | Cloudflare 完整教學
上一篇我們鳥瞰了整片地圖,這一篇正式動手。我們會用
npm create cloudflare建立專案,看懂 export default 裡的 fetch handler,用 wrangler dev 在本地跑起來,最後把你的第一個 Cloudflare Worker 部署到全球邊緣網路,並觀察每一個進來的 request。
前言
本篇要做的事只有一句話:從零建立一個 Cloudflare Worker,讓它在本地跑起來,再部署到全球邊緣網路上線。
如果你曾經架過傳統伺服器,大概記得那套流程:租一台機器、裝作業系統、設定 Nginx、開防火牆、綁網域、還要煩惱擴展。Cloudflare Workers 把這一切壓縮成兩行指令——wrangler dev 在本地開發、wrangler deploy 上線。這就像從「自己蓋一間店面、拉水電、請店員」變成「走進共享廚房,插上電就能開始出餐」:基礎設施早就替你準備好,你只要專心寫回應請求的邏輯。
上一篇《開發者平台總覽》是廣度優先的地圖,這一篇則是實作優先的第一步。讀完本篇,你會掌握:
- 用
npm create cloudflare(C3)或wrangler init建立一個 Worker 專案,看懂它產生的檔案結構 export default { fetch }這個進入點的每個參數是什麼、怎麼運作- 用
wrangler dev在本地即時開發、除錯,再用wrangler deploy一鍵上線 - 寫出基礎路由(依路徑回不同內容)並觀察
request上的邊緣中繼資料
核心概念
一個 Worker 專案長什麼樣
用 C3 建立完專案後,你會得到一個結構非常精簡的目錄。核心其實只有三個東西:
my-first-worker/
├── src/
│ └── index.ts ← 你的 Worker 程式碼(fetch handler 在這)
├── wrangler.jsonc ← 設定檔:名稱、進入點、相容性日期、Bindings
├── package.json ← 依賴與 npm scripts
├── tsconfig.json ← TypeScript 設定
└── worker-configuration.d.ts ← 自動生成的型別(Env 介面等)
比起傳統後端專案動輒數十個資料夾,這裡乾淨得驚人。原因很簡單:作業系統、HTTP 伺服器、路由框架、程序管理——這些在傳統架構要你自己組裝的東西,Cloudflare 的執行環境全都內建了。你唯一需要關心的,是 src/index.ts 裡「請求進來後要回什麼」。
請求生命週期:一個 request 怎麼被處理
在寫程式之前,先在腦中建立一張請求流動的圖。當使用者對你的 Worker 發出一個 HTTP 請求時,大致經過這幾步:
使用者發出請求
│
▼
┌─────────────────────────────────────────────┐
│ Cloudflare 邊緣網路 │
│ ① 請求抵達離使用者最近的 PoP 節點 │
│ ② 路由系統匹配到你的 Worker │
│ ③ 重用現有 Isolate(熱啟動) │
│ 或建立新的 Isolate(冷啟動 < 1ms) │
│ │ │
│ ▼ │
│ ④ Runtime 呼叫你的 fetch handler: │
│ fetch(request, env, ctx) │
│ │ │
│ ├─ 讀 request(URL、method、headers) │
│ ├─ 執行你的邏輯(路由、查資料...) │
│ └─ return new Response(...) │
│ │ │
│ ▼ │
│ ⑤ 回應即刻串流回使用者 │
└─────────────────────────────────────────────┘
整個過程的重點是第 ④ 步:Runtime 傳給你三個參數 request、env、ctx,你回傳一個 Response,就這樣。 你不需要處理 socket、不需要解析 HTTP 協定、不需要啟動伺服器——這些底層工作 Cloudflare 都做完了,遞到你手上的已經是一個乾淨的標準 Web Request 物件。
三個關鍵術語
- fetch handler:處理 HTTP 請求的進入點函式,也是最常用的 handler。除了
fetch,Workers 還有scheduled(Cron 定時)、queue(消費佇列)、email(收信)等 handler,後續文章會逐一介紹。 - wrangler:Cloudflare 官方 CLI 工具,負責建立、開發、部署與管理 Worker。你會反覆用到
wrangler dev與wrangler deploy。 - compatibility_date:設定檔裡的相容性日期,決定你的 Worker 使用哪個版本的 runtime 行為,是確保部署穩定可重現的關鍵。
實作範例
概念講完,我們正式動手。整個流程分成四步:建立專案 → 寫 fetch handler → 本地開發 → 部署上線。
步驟一:建立專案
開啟終端機,執行官方推薦的 C3(create-cloudflare)指令。這是 2026 年建立 Worker 專案的標準方式:
# 建立一個名為 my-first-worker 的 Worker 專案
npm create cloudflare@latest my-first-worker
指令會進入互動式選單,第一個 Worker 建議這樣選:
- What would you like to start with? → 選
Hello World example(最乾淨的起點) - Which template would you like to use? → 選
Worker only - Which language do you want to use? → 選
TypeScript - Do you want to deploy your application? → 先選
No,我們稍後手動部署
C3 會自動安裝依賴、初始化 Git、並產生標準的 wrangler.jsonc。
如果你偏好更精簡的方式,也可以在既有的空目錄裡用舊的
wrangler init指令。不過 C3 範本更完整、預設值更貼近 2026 年的最佳實踐,新專案一律建議用npm create cloudflare。
步驟二:看懂並改寫 fetch handler
打開 src/index.ts,把內容改成下面這個帶基礎路由的版本。這段程式碼示範了 fetch handler 的完整樣貌:
// src/index.ts
// export default 匯出一個物件,裡面的 fetch 就是處理 HTTP 請求的進入點。
// 這是 ES Modules 語法,也是 Cloudflare 官方唯一推薦的寫法。
export default {
// 三個參數:
// request — 進來的 HTTP 請求(標準 Web Request 物件)
// env — 所有 Bindings 的入口(KV、D1、AI... 都掛在這,本篇還沒用到)
// ctx — 執行上下文,可用 ctx.waitUntil() 跑背景任務
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// 用標準的 URL 物件解析請求網址,取出路徑
const url = new URL(request.url);
// 路由一:首頁,回傳一段純文字
if (url.pathname === "/") {
return new Response("Hello from the edge! 你好,你的第一個 Worker 上線了。", {
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
}
// 路由二:/api/hello,回傳 JSON,並觀察這次請求的邊緣中繼資料
if (url.pathname === "/api/hello") {
const data = {
message: "Hello, Worker!",
method: request.method, // 請求方法:GET / POST...
colo: request.cf?.colo ?? "unknown", // 處理此請求的 PoP 代碼(如 TPE)
country: request.cf?.country ?? "unknown", // 使用者所在國家
timestamp: new Date().toISOString(),
};
return new Response(JSON.stringify(data, null, 2), {
headers: { "Content-Type": "application/json; charset=utf-8" },
});
}
// 其他路徑:回傳 404
return new Response("Not Found", { status: 404 });
},
} satisfies ExportedHandler<Env>;
逐段拆解:
export default { ... }:這是 ES Modules 語法。舊的 Service Worker 寫法(addEventListener('fetch', ...))官方已標示為棄用(Deprecated),新專案一律用這種。async fetch(request, env, ctx):fetch是處理 HTTP 請求的 handler。request是標準的 WebRequest、env是所有 Bindings 的入口、ctx用來跑背景任務。new URL(request.url):用瀏覽器與 Node 通用的標準URL物件解析網址,url.pathname就是路徑。所謂「路由」的本質,就是根據pathname與request.method決定回什麼——一開始不需要任何框架,幾個if就夠了。request.cf?.colo:Cloudflare 在每個請求上附帶的中繼資料,colo是處理這次請求的 PoP 代碼。這正是「邊緣運算」最直接的證據:你的程式碼真的在離使用者最近的節點上執行。request.cf還帶有country、city、timezone等豐富資訊,非常適合做地區化邏輯。satisfies ExportedHandler<Env>:讓 TypeScript 幫你檢查 handler 的型別簽名是否正確,同時保留完整的 IntelliSense 自動補全。
步驟三:設定檔 wrangler.jsonc
打開 C3 產生的 wrangler.jsonc。2026 年 Cloudflare 已全面改用 wrangler.jsonc(取代舊的 wrangler.toml),支援註解、可讀性更好。內容大致如下:
{
// $schema 提供編輯器自動補全與驗證
"$schema": "./node_modules/wrangler/config-schema.json",
// Worker 的名稱,也會成為預設網址的一部分
"name": "my-first-worker",
// 進入點:指向你的 fetch handler 檔案
"main": "src/index.ts",
// compatibility_date 決定 Worker 使用的 runtime 行為版本
// 新專案建議設為建立當天的日期,C3 會自動填好
"compatibility_date": "2026-08-02",
// 開啟可觀測性,之後在 Dashboard 就能看到結構化日誌
"observability": { "enabled": true }
}
其中 compatibility_date 是新手最容易忽略、卻最重要的一行。它像是替你的 Worker「凍結」了一個 runtime 行為的版本:Cloudflare 之後即使更新執行環境,你的 Worker 也會依照這個日期的行為運作,不會某天突然壞掉。
步驟四:本地開發與部署上線
先在本地把 Worker 跑起來。在專案目錄執行:
# 本地開發:在 http://localhost:8787 模擬完整的 Workers 執行環境
npx wrangler dev
wrangler dev 底層使用一個叫 Miniflare 的模擬器,能在你自己的電腦上重現完整的 Workers 執行環境。啟動後打開瀏覽器造訪 http://localhost:8787/,你會看到首頁那段歡迎文字;再造訪 http://localhost:8787/api/hello,就能看到 JSON 回應。這個階段改任何程式碼都會即時生效、不會動到線上資源,是寫程式與除錯的主場。
在終端機的互動視窗裡,按 b 可以自動開啟瀏覽器、按 d 開啟遠端除錯器、按 l 切換本地/遠端模式。確認一切正常後,只要一行指令就能部署到全球:
# 部署到 Cloudflare 全球邊緣網路
npx wrangler deploy
第一次執行 deploy 時,wrangler 會引導你用瀏覽器登入 Cloudflare 帳號授權(OAuth)。授權完成後,它會打包你的 Worker、上傳,並回傳一個 *.workers.dev 的網址,例如:
# 部署成功後的輸出(示意)
Total Upload: 1.23 KiB / gzip: 0.58 KiB
Uploaded my-first-worker (2.34 sec)
Deployed my-first-worker triggers (0.31 sec)
https://my-first-worker.<你的子網域>.workers.dev
打開那個網址,你的第一個 Worker 就同時活在全球 330+ 個節點上了——不需要設定伺服器、不需要選區域、不需要配置負載平衡。造訪 /api/hello,看看 colo 欄位顯示的節點代碼,那就是此刻替你服務的邊緣節點。
常見錯誤與最佳實踐
第一次寫 Worker,最常踩的幾個坑如下。先認得它們,能省下大把除錯時間。
坑一:忘記或亂設 compatibility_date。
如果 wrangler.jsonc 缺少 compatibility_date,部署時 wrangler 會警告;而如果你隨手填一個很舊的日期,某些現代 API 可能無法使用。正確做法是設為專案建立當天的日期(C3 會自動幫你填),需要新功能時再依官方指引往後調整。
坑二:搞混「本地」與「遠端」。
wrangler dev 預設是本地模式,KV、D1 等資源都是 Miniflare 的本地模擬版,資料不會同步到線上。很多新手在本地寫入了資料,卻到線上找不到,就是這個原因。反過來,wrangler deploy 動到的是正式資源。心法很簡單:開發階段一切在本地,deploy 才碰真實環境。 若真要在本地連到遠端資源測試,才用 wrangler dev --remote。
坑三:以為模組層級變數能保存狀態。
因為承載 Worker 的 Isolate 隨時可能被回收重建,模組層級的可變變數不可靠:
// ❌ 危險:requestCount 可能在任意請求後被重置為 0
let requestCount = 0;
export default {
async fetch(): Promise<Response> {
requestCount++; // 這個數字完全不可信
return new Response(`Count: ${requestCount}`);
},
};
// ✅ 正確:持久狀態要存進 KV、D1 或 Durable Objects
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const count = parseInt((await env.COUNTERS.get("requests")) ?? "0") + 1;
await env.COUNTERS.put("requests", String(count));
return new Response(`Count: ${count}`);
},
};
反過來說,不可變的常數(例如編譯好的正規表達式、設定物件)放在模組層級是安全且推薦的,能跨請求重用、提升效能。這一點會在下一篇《V8 Isolates 執行模型》詳細展開。
坑四:fetch handler 沒有回傳 Response。
fetch 必須回傳一個 Response(或 Promise<Response>)。忘記 return、或在某條路由分支沒有回應,執行期就會拋錯。養成習慣:每一條路由分支都明確 return,並在最後補一個預設的 404 回應當作兜底。
一條實用的入門心法:把 Worker 想成一個「輸入 Request、輸出 Response」的純函式。 只要每次請求都能穩定地由 request 算出 Response、不依賴任何跨請求的可變狀態,你的 Worker 就會既好懂又可靠。
小結
這一篇,我們把上一篇《開發者平台總覽》的地圖化為實際行動,完整走過建立並部署第一個 Worker 的流程:
- 用
npm create cloudflare(C3)建立專案,看懂src/index.ts與wrangler.jsonc的角色。 - 寫出
export default { fetch }進入點,用幾個if就實現了基礎路由,並透過request.cf.colo觀察處理請求的邊緣節點。 - 用
wrangler dev在本地即時開發,再用wrangler deploy一鍵把 Worker 推上全球 330+ 個節點。 - 認得四個新手常見坑,並建立「Worker 是 Request 進、Response 出的純函式」這個關鍵心智模型。
你或許會好奇:為什麼 Worker 的冷啟動可以小於 1ms?為什麼模組層級變數不可靠?這一切的答案都藏在它的執行模型裡。下一篇《V8 Isolates 執行模型》,我們會拆開 Workers 的引擎蓋,看看 V8 Isolates 到底是什麼、它如何做到近乎零的冷啟動,以及這個模型帶來哪些你必須知道的行為與限制。
想深入官方細節,可以隨時參考 Cloudflare Workers 官方入門指南。準備好了嗎?我們下一篇深入引擎內部。