Vercel Edge Functions 完整教學|2025 邊緣運算入門指南
想要更快的 API 回應?
Edge Functions 在全球各地的邊緣節點執行,離使用者最近。
冷啟動幾乎為零,延遲極低。
這篇文章教你如何使用 Vercel Edge Functions。
什麼是 Edge Functions?
Edge Functions 是在邊緣網路節點執行的輕量級函數。
Edge vs Serverless 比較
| 項目 | Edge Functions | Serverless Functions |
|---|---|---|
| 執行環境 | V8 Runtime | Node.js |
| 冷啟動 | 幾乎為零 | 200-500ms |
| 執行時間限制 | 30 秒 | 10s-900s |
| 記憶體 | 128 MB | 最多 3 GB |
| Node.js API | 有限(Web APIs) | 完整支援 |
| 全球節點 | 100+ | 依區域設定 |
什麼時候用 Edge Functions?
適合:
- 需要最低延遲的 API
- 簡單的資料處理
- A/B 測試
- 地理位置判斷
- 認證和權限檢查
- 請求重寫和重導向
不適合:
- 需要完整 Node.js API
- 長時間運算
- 大量記憶體需求
- 需要特定 Node.js 套件
快速開始
建立 Edge Function
// app/api/hello/route.ts
export const runtime = 'edge'; // 關鍵:指定 edge runtime
export async function GET() {
return Response.json({
message: 'Hello from the Edge!',
timestamp: new Date().toISOString(),
});
}
測試延遲
// app/api/ping/route.ts
export const runtime = 'edge';
export async function GET() {
return Response.json({
pong: true,
region: process.env.VERCEL_REGION, // 顯示執行的區域
});
}
部署後,你會發現從全球各地訪問都很快。
常見用例
用例一:地理位置判斷
// app/api/geo/route.ts
export const runtime = 'edge';
export async function GET(request: Request) {
// Vercel 自動注入地理位置資訊
const country = request.headers.get('x-vercel-ip-country') || 'Unknown';
const city = request.headers.get('x-vercel-ip-city') || 'Unknown';
const region = request.headers.get('x-vercel-ip-country-region') || 'Unknown';
return Response.json({
country,
city,
region,
});
}
用例二:A/B 測試
// app/api/ab-test/route.ts
export const runtime = 'edge';
export async function GET(request: Request) {
const url = new URL(request.url);
// 從 cookie 取得或分配測試群組
const cookies = request.headers.get('cookie') || '';
let variant = cookies.match(/ab-variant=(\w)/)?.[1];
if (!variant) {
variant = Math.random() > 0.5 ? 'A' : 'B';
}
return new Response(null, {
status: 302,
headers: {
'Location': variant === 'A' ? '/page-a' : '/page-b',
'Set-Cookie': `ab-variant=${variant}; Path=/; Max-Age=86400`,
},
});
}
用例三:認證檢查
// app/api/auth-check/route.ts
import { jwtVerify } from 'jose';
export const runtime = 'edge';
export async function GET(request: Request) {
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return Response.json(
{ error: 'Missing token' },
{ status: 401 }
);
}
const token = authHeader.split(' ')[1];
try {
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const { payload } = await jwtVerify(token, secret);
return Response.json({
valid: true,
userId: payload.sub,
});
} catch {
return Response.json(
{ error: 'Invalid token' },
{ status: 401 }
);
}
}
用例四:API 快取代理
// app/api/cached-data/route.ts
export const runtime = 'edge';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get('id');
// 呼叫外部 API
const res = await fetch(`https://api.example.com/data/${id}`, {
next: { revalidate: 60 }, // 60 秒快取
});
const data = await res.json();
return Response.json(data, {
headers: {
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=600',
},
});
}
用例五:請求重寫
// middleware.ts(Middleware 也使用 Edge Runtime)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const url = request.nextUrl.clone();
// 根據語言重寫路徑
const lang = request.headers.get('Accept-Language')?.split(',')[0] || 'en';
if (lang.startsWith('zh')) {
url.pathname = `/zh${url.pathname}`;
} else {
url.pathname = `/en${url.pathname}`;
}
return NextResponse.rewrite(url);
}
export const config = {
matcher: ['/((?!api|_next|favicon.ico).*)'],
};
Edge Functions 的限制
不支援的 Node.js API
Edge Functions 使用 V8 Runtime,不是 Node.js。
不支援的 API:
// ❌ 不支援
import fs from 'fs';
import path from 'path';
import crypto from 'crypto'; // 部分支援
import child_process from 'child_process';
支援的 API:
// ✅ 支援
fetch()
Request, Response
Headers
URL, URLSearchParams
TextEncoder, TextDecoder
crypto.subtle // Web Crypto API
setTimeout, setInterval
console.log
套件限制
// ❌ 不支援完整 Node.js 的套件
import bcrypt from 'bcrypt'; // 需要 native addon
// ✅ 使用 Web 相容的替代品
import { hashPassword } from '@noble/hashes/pbkdf2';
常用替代品:
| 原套件 | Edge 替代品 |
|---|---|
| bcrypt | @noble/hashes |
| jsonwebtoken | jose |
| uuid | crypto.randomUUID() |
記憶體限制
Edge Functions 只有 128 MB 記憶體,不適合:
- 大型資料處理
- 圖片處理
- 複雜運算
與 Serverless Functions 混合使用
架構建議
用戶請求
│
▼
Edge Function(快速檢查)
│
├─ 驗證失敗 → 回傳 401
│
├─ 簡單請求 → 直接回應
│
└─ 複雜請求 → 轉發到 Serverless Function
實作範例
// app/api/data/route.ts (Edge)
export const runtime = 'edge';
export async function GET(request: Request) {
// 快速驗證
const authHeader = request.headers.get('Authorization');
if (!authHeader) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
// 簡單請求直接處理
const { searchParams } = new URL(request.url);
if (searchParams.get('simple') === 'true') {
return Response.json({ simple: 'data' });
}
// 複雜請求轉發到 Serverless
const res = await fetch(
`${process.env.VERCEL_URL}/api/data-heavy?${searchParams.toString()}`,
{ headers: request.headers }
);
return res;
}
// app/api/data-heavy/route.ts (Serverless)
// 沒有 runtime = 'edge',使用預設的 Node.js
export async function GET(request: Request) {
// 複雜的資料庫查詢
const data = await complexDatabaseQuery();
return Response.json(data);
}
效能優化
最小化套件大小
// ✅ 只引入需要的
import { jwtVerify } from 'jose';
// ❌ 不要引入整個套件
import * as jose from 'jose';
使用快取
export const runtime = 'edge';
export async function GET() {
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 300 }, // 5 分鐘快取
});
return new Response(await data.text(), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, s-maxage=300',
},
});
}
避免不必要的計算
export const runtime = 'edge';
// 在模組層級初始化(只執行一次)
const CONFIG = {
apiUrl: process.env.API_URL,
apiKey: process.env.API_KEY,
};
export async function GET() {
// 直接使用,不用每次重新讀取
const res = await fetch(CONFIG.apiUrl, {
headers: { 'Authorization': CONFIG.apiKey },
});
return res;
}
偵錯和監控
本機測試
vercel dev
# Edge Functions 會模擬 edge 環境
查看執行區域
export const runtime = 'edge';
export async function GET() {
return Response.json({
region: process.env.VERCEL_REGION,
edge: true,
});
}
記錄 Log
export const runtime = 'edge';
export async function GET(request: Request) {
console.log('Request from:', request.headers.get('x-forwarded-for'));
console.log('User-Agent:', request.headers.get('user-agent'));
// Log 會出現在 Vercel Dashboard
return Response.json({ logged: true });
}
常見問題 FAQ
Q1:Edge Function 和 Middleware 有什麼不同?
Middleware:
- 在每個請求前執行
- 用於全站的邏輯(認證、重寫)
- 放在
middleware.ts
Edge Function:
- 特定 API 路由
- 用於 API 邏輯
- 放在
app/api/下
兩者都使用 Edge Runtime。
Q2:為什麼我的套件不能用?
Edge Runtime 不是 Node.js,很多套件不相容。
解決方法:
- 尋找 Web 相容的替代品
- 改用 Serverless Function
- 只在 Edge 做簡單邏輯
Q3:Edge Function 可以連資料庫嗎?
可以,但有限制:
- 需要 HTTP 連線的資料庫(不支援 TCP)
- Vercel Postgres 支援
- PlanetScale 支援
- Supabase 支援
import { sql } from '@vercel/postgres';
export const runtime = 'edge';
export async function GET() {
const { rows } = await sql`SELECT * FROM users LIMIT 10`;
return Response.json(rows);
}
Vercel 部署失敗?
Build Error、環境變數、自訂網域,我們幫你快速排除問題。