Vercel Edge Functions 完整教學|2025 邊緣運算入門指南

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,很多套件不相容。

解決方法:

  1. 尋找 Web 相容的替代品
  2. 改用 Serverless Function
  3. 只在 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、環境變數、自訂網域,我們幫你快速排除問題。

解決 Vercel 問題


延伸閱讀

分享文章:
V

VibeFix

專門解決 AI Vibe Coding 後的疑難雜症,讓你的專案順利上線。

這篇文章有幫到你嗎?

如果還有問題,讓我們直接幫你解決!

聯繫我們