第五章:Edge Functions 部署實戰:讓 WebSocket 連線又快又穩,突破 Vercel 限制

你的 AI 聊天室在 `localhost` 本地開發環境跑得飛快、打字機特效如絲般順滑,於是你開心地將程式碼部署上了 Vercel 準備向客戶展示。結果一上線,真正的災難才剛開始:

  1. AI 回答有時候會卡住,過很久才突然吐出一大段字。
  2. 聊到一半如果 AI 生成超過 10 秒鐘,Vercel 直接中斷連線,噴出 `504 Gateway Timeout` 的紅色錯誤。
  3. 第一個使用者發問時總是特別慢,要等好幾秒 AI 才開始回話。

這是 99% 的新手在第一次部署 AI 產品時都會撞破頭的牆。本章將為你深度解密為什麼會發生這種事,以及如何透過「Edge Runtime (邊緣運算)」來突破物理與雲端架構的極限限制。


💀 殺手一號:Serverless Function 的「最大執行時間 (Max Duration)」限制

現代的前端框架 (如 Next.js) 部署在 Vercel 上時,預設的後端 API 運算環境叫做 Node.js Serverless Function (無伺服器函式)

Serverless 的商業模式是「算次數與時間」收費的,所以雲端平台為了防止你的程式碼寫出無窮迴圈把機房燒掉,設定了嚴格的超時限制:

  • 在 Vercel 的免費版 (Hobby Tier) 中,一個 API Route 最多只能跑 10 秒
  • 在 Vercel 的付費版 (Pro Tier) 中,最長預設為 15 秒 (最多可上調至 300 秒,但費用驚人)。

這跟 AI 聊天室有什麼關係? 當使用者問了一個需要深度思考的複雜問題,AI 模型 (如 GPT-4 或 Claude Opus) 可能需要 25 秒才能把整篇文章生成完並 Streaming 結束。 結果,當你的 Streaming 跑到第 10 秒鐘時,Vercel 的伺服器無情地說:「時間到,我下班了!」,直接強制切斷你的 API Route。 前端收不到完整的結束訊號,打字機就會突然卡死,或是直接噴出 Error。這就是產生 Timeout 與斷線的頭號主因。

❄️ 殺手二號:冷啟動 (Cold Start) 與跨太平洋的地理延遲

Serverless 的另一個特性是「沒人用的時候會休眠 (Scale to Zero)」。 當深夜沒有流量時,你的伺服器是不存在的。 當早上第一位用戶發送訊息時,Vercel 需要花 1~2 秒把伺服器「喚醒 (Cold Start)」,再去敲 OpenAI 的 API,然後 OpenAI 把資料傳回美國的 Vercel 伺服器,Vercel 再把資料跨越太平洋傳回台灣使用者的瀏覽器上。

這種「喚醒延遲」加上「多層次跨國網路傳輸」,會讓 Streaming 的體驗變得非常糟糕:打字機特效會變得「卡卡的、字一塊一塊噴出來,而不是平滑地逐字印出」。


💊 解藥降臨:Vercel Edge Runtime (邊緣運算節點)

為了解決這個 Timeout 與延遲的問題,Vercel (以及 Next.js 官方) 推出了革命性的底層架構:Edge Runtime。 你可以把 Edge Runtime 想像成「超級輕量化、散佈在全球各大節點的微型伺服器」。

Edge Runtime 的三大絕對優勢:

  1. 沒有 10 秒超時限制:只要你的 Streaming 資料持續在傳輸(即使傳得很慢),Edge 就不會中斷連線!它允許你的 AI 慢慢講話講超過一分鐘都沒問題。
  2. 零冷啟動 (Zero Cold Start):Edge 節點極度輕量化(底層是 V8 Isolate 而不是完整的 Node.js),它可以做到隨傳隨到,不用等伺服器開機。
  3. 離用戶超級近:Edge 節點遍佈全球(包括台北、東京)。資料從 OpenAI 傳到亞洲節點,再直接推給台灣用戶,物理距離的縮短讓延遲大幅下降。

🚀 如何將你的 API Route 切換成 Edge?

這簡單到令人髮指,這就是框架的威力。你只需要在處理 AI Streaming 的 Route Handler 檔案 (例如 `src/app/api/chat/route.ts`) 的最上方,加上一行宣告:

```typescript import { NextResponse } from 'next/server';

// 🚀 加上這行魔法指令:告訴 Next.js 這個 API 不要跑在美國的 Node.js 伺服器 // 而是要部署在全球 CDN 的邊緣節點上! export const runtime = 'edge';

export async function POST(req: Request) { // 1. 取得使用者訊息 const { message } = await req.json();

// 2. 呼叫 OpenAI API,並設定為 stream 模式 // (由於我們在 Edge 環境,所有的 fetch 都享有全球邊緣節點的極速優勢) const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, }, body: JSON.stringify({ model: 'gpt-4', stream: true, // 核心!要求串流回傳 messages: [{ role: 'user', content: message }], }), });

// 3. 直接將 ReadableStream 轉交給前端 return new Response(response.body, { headers: { 'Content-Type': 'text/event-stream', // 重要:告訴瀏覽器這是一條長連線 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }, }); } ```

加上 `export const runtime = 'edge'` 並重新部署後,你的 AI API 將會脫胎換骨,突破 10 秒的 Timeout 死亡魔咒,享受毫秒級的延遲與如絲般順滑的打字體驗!


💣 Edge Runtime 的致命限制與踩坑警告

別高興得太早,既然 Edge 這麼神,為什麼 Next.js 不全部預設用 Edge 就好了?

因為 Edge 環境「超級精簡」。為了追求極致的速度與輕量,它不是一個完整的 Node.js 環境。 這意味著:

  1. 不能在 Edge 裡面使用 `fs` (檔案系統) 模組去讀取本機檔案。
  2. 不能使用 `child_process` 執行終端機指令。
  3. 不能使用很多依賴 Node 原生 C++ 模組的 NPM 套件(例如舊版的 `bcrypt` 加密套件、或是需要複雜驅動程式的舊式關聯式資料庫 ORM)。

經典連環地雷:Prisma 與 Edge 的衝突

如果你在 Edge API Route 裡面嘗試 `import { PrismaClient } from '@prisma/client'` 去讀寫關聯式資料庫,你的程式會在上線後直接崩潰。因為傳統 Prisma 需要依賴底層的 C++ 引擎 (`Query Engine`) 建立 TCP 連線到資料庫,這在輕薄短小的 Edge 裡根本跑不起來。

架構師的終極解決方案

  1. 職責分離 (Micro-architecture):讓負責「打字機 Streaming」的 API 保持在 Edge Runtime 以確保速度。等對話全部結束後,再透過前端發出另一個獨立的 HTTP 請求到「普通的 Node API」,把完整的對話紀錄寫進資料庫裡。
  2. 使用 Edge-compatible 資料庫服務:這也是為什麼在現代 SaaS 開發中,我們極度推崇 Supabase。因為它的 `@supabase/supabase-js` SDK 底層完全是基於標準的 `fetch` HTTP 請求,所以它 完美相容於 Edge Runtime! 你可以在 Edge 裡面直接讀寫 Supabase 資料庫,暢通無阻。

🎉 課程結業總結

即時連線與 Streaming 是一個極度深奧的後端深水區,它徹底打破了傳統 Request-Response 的思維定勢。

讓我們回顧一下你在這五個章節中學到了多麼強大的技術:

  1. 底層概念:從 HTTP Polling 的痛點,到 WebSocket 與 SSE 串流的崛起。
  2. WebSocket 實戰:學會使用 Supabase Realtime 輕鬆監聽資料庫,實現免重新整理的畫面推播。
  3. 前端解碼:親手刻出處理 ReadableStream 與 TextDecoder 的核心解碼迴圈。
  4. 狀態管理:拋棄了效能低下的 Context API,導入業界標配的 Zustand 與 localStorage 打造極速的對話紀錄切換。
  5. 雲端架構:看透了 Vercel Serverless 的 10 秒 Timeout 限制,並成功掛載 Edge Runtime 邊緣運算節點。

只要你打通了這五個關卡,你就不再是只能寫切版和靜態網頁的初階開發者了。你已經具備了打造百萬級併發、極致絲滑的現代化 AI SaaS 商業級產品的能力!去創造屬於你的偉大產品吧!


🚀 Edge Functions 實戰技巧

Cloudflare Workers + WebSocket

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const upgradeHeader = request.headers.get("Upgrade");
    if (upgradeHeader !== "websocket")
      return new Response("Expected WebSocket", { status: 426 });

    const webSocketPair = new WebSocketPair();
    const [client, server] = Object.values(webSocketPair);
    server.accept();
    server.addEventListener("message", async (event) => {
      const message = JSON.parse(event.data);
      const aiResponse = await fetch("https://api.openai.com/v1/chat/completions", {
        method: "POST",
        headers: { Authorization: `Bearer ${env.OPENAI_API_KEY}` },
        body: JSON.stringify({ model: "gpt-4o-mini", messages: message.messages })
      });
      const data = await aiResponse.json();
      server.send(JSON.stringify({ type: "ai-response", content: data.choices[0].message.content }));
    });
    return new Response(null, { status: 101, webSocket: client });
  }
}

🌐 Edge Functions vs 傳統 Serverless

| 比較 | AWS Lambda / Vercel | Edge Functions (Workers) | |------|:-------------------:|:------------------------:| | 執行位置 | 特定區域資料中心 | 全球 30+ 邊緣節點 | | 冷啟動 | 100-1000ms | < 5ms | | 最長執行時間 | 15 分鐘 | 30 秒(免費)/ 15 分鐘(付費) | | WebSocket | ❌ 不支援長連線 | ✅ 原生支援 | | 記憶體 | 128MB - 10GB | 128MB | | 檔案系統 | ✅ 可寫入 /tmp | ❌ 唯讀 |

部署檢查清單

- [ ] WebSocket 端點有設定連線數限制
- [ ] 有實作心跳 (Heartbeat) 機制
- [ ] 連線中斷時有自動重連邏輯
- [ ] 歷史訊息有做分頁載入
- [ ] AI API 有設定 timeout
- [ ] 環境變數已設定(OPENAI_API_KEY 等)
- [ ] CORS 設定正確


WebSocket 即時通訊實戰

為什麼需要 WebSocket?

傳統 HTTP API 是「請求-回應」模式——前端發請求,後端回資料。但 AI 對話需要即時的雙向通訊——AI 不是一次回完整段話,而是逐字或逐句回傳。

WebSocket vs HTTP Streaming

| 比較 | WebSocket | HTTP Streaming | |:----|:---------|:--------------| | 連線方式 | 升級連線(ws://) | 一般 HTTP(長連線) | | 雙向通訊 | ✅ 雙向 | ❌ 單向(伺服器→客戶端) | | 相容性 | 需要 WebSocket 協定 | 一般 HTTP 即可 | | AI 對話適合 | ✅ 適合 | 也可用 SSE |

實作 WebSocket Server

from fastapi import FastAPI, WebSocket

app = FastAPI()

@app.websocket('/ws/chat')
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        # 接收使用者訊息
        data = await websocket.receive_text()
        
        # 呼叫 AI 並逐字回傳
        async for chunk in ai_stream_response(data):
            await websocket.send_text(chunk)

課程總結

這堂 WebSocket AI Chat 課程從 WebSocket 基礎、即時通訊、AI 串接到完整聊天應用——你現在可以建立具有即時 AI 對話能力的應用。

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!