Edge Functionマスターガイド2025 - エッジコンピューティングの完全攻略
Cloudflare Workers、Vercel Edge Functions、Deno Deployの最新機能を徹底比較。パフォーマンス最適化、コスト削減、実装パターン、本番運用のベストプラクティスまで、エッジコンピューティングを極める完全ガイドです。
Edge Functionの基礎から実践的な活用方法まで徹底解説。Cloudflare Workers、Vercel Edge Functionsの比較、認証・API Gateway・パーソナライゼーションの実装例、パフォーマンス最適化まで、エッジコンピューティングを活用した高速なWebアプリケーション構築に必要な全知識を網羅。
エッジコンピューティングの進化により、Web アプリケーションのパフォーマンスとユーザー体験は新たな次元に到達しました。2025 年現在、Edge Function は単なるパフォーマンス最適化ツールを超えて、認証、パーソナライゼーション、リアルタイム処理など、多様なユースケースに対応する必須技術となっています。本記事では、主要プラットフォームの最新機能比較から実践的な実装パターンまで、包括的に解説します。
Edge Function は、CDN のエッジロケーション上で実行されるサーバーレス関数です。ユーザーに最も近い場所でコードを実行することで、レイテンシを大幅に削減し、グローバルスケールでの高速なレスポンスを実現します。
特徴 | 従来のサーバーレス | Edge Function |
---|---|---|
実行場所 | 特定リージョンのデータセンター | グローバルCDNエッジ |
レイテンシ | 50-500ms | 5-50ms |
コールドスタート | あり(100-1000ms) | ほぼなし(5-50ms) |
実行時間制限 | 最大15分 | 最大50ms-30秒 |
メモリ制限 | 最大10GB | 128MB-256MB |
ユースケース | バッチ処理・重い計算 | リアルタイム処理・軽量タスク |
チャートを読み込み中...
2025 年 6 月、Cloudflareは大きな転換点を迎えます。
静的アセットの配信が大幅に高速化
ダッシュボード内での完全な検索・フィルタリング機能
Workersとコンテナのハイブリッドワークロードをサポート
Float16Array対応など最新のJavaScript機能
Service Bindings と Tail Workers へのリクエストが無料化され、マイクロサービスアーキテクチャの構築コストが大幅に削減されました。
Vercelは「Fluid Compute」モデルの導入により、パフォーマンスを新たなレベルに引き上げました。
主要機能:
項目 | Cloudflare Workers | Vercel Edge Functions |
---|---|---|
グローバル展開 | 200+ロケーション | 20+リージョン |
実行時間制限 | 30秒(有料)/10秒(無料) | 25秒初期レスポンス後は無制限 |
メモリ制限 | 128MB | 256MB(Netlify経由) |
CPU時間制限 | 50ms(無料)/30秒(有料) | なし(初期レスポンス内) |
言語サポート | JavaScript, TypeScript, Rust, C++ | JavaScript, TypeScript |
価格 | $5/月〜 + 従量課金 | $20/月〜 + 従量課金 |
開発体験 | Wrangler CLI, Web IDE | Vercel CLI, フレームワーク統合 |
特徴 | コンテナサポート(2025年6月) | Fluid Compute, Next.js統合 |
興味深いことに、Vercel Edge Functions は Cloudflare Workers の上に構築されています。しかし、それぞれ独自の最適化と機能を提供しています。
# Wrangler CLIのインストール
npm install -g wrangler
# プロジェクトの初期化
wrangler init my-worker
# ローカル開発
wrangler dev
# デプロイ
wrangler deploy
# Vercel CLIのインストール
npm install -g vercel
# Next.jsプロジェクトの作成
npx create-next-app@latest my-edge-app
# Edge Functionの作成
# app/api/edge/route.ts または pages/api/edge.ts
# デプロイ
vercel
Edge Function を使用した認証システムは、グローバルに低レイテンシでユーザー認証を提供できます。
Edge Function を API Gateway として使用することで、認証、レート制限、ルーティング、キャッシングなどを統一的に管理できます。
チャートを読み込み中...
// Cloudflare Workers: API Gateway実装
interface GatewayEnv {
RATE_LIMIT_KV: KVNamespace;
CACHE_KV: KVNamespace;
API_KEYS: string;
BACKEND_URLS: string;
}
interface BackendConfig {
[key: string]: {
url: string;
timeout: number;
cache?: {
ttl: number;
key: (request: Request) => string;
};
};
}
export default {
async fetch(
request: Request,
env: GatewayEnv,
ctx: ExecutionContext
): Promise<Response> {
// CORSヘッダーの設定
if (request.method === 'OPTIONS') {
return handleCORS();
}
// レート制限のチェック
const rateLimitResult = await checkRateLimit(
request,
env.RATE_LIMIT_KV
);
if (!rateLimitResult.allowed) {
return new Response('Too Many Requests', {
status: 429,
headers: {
'Retry-After': rateLimitResult.retryAfter.toString()
}
});
}
// APIキー認証
const apiKey = request.headers.get('X-API-Key');
if (!apiKey || !env.API_KEYS.includes(apiKey)) {
return new Response('Unauthorized', { status: 401 });
}
// バックエンドの設定
const backends: BackendConfig = JSON.parse(env.BACKEND_URLS);
const url = new URL(request.url);
const [, service, ...pathParts] = url.pathname.split('/');
const backend = backends[service];
if (!backend) {
return new Response('Service not found', { status: 404 });
}
// キャッシュチェック
if (request.method === 'GET' && backend.cache) {
const cacheKey = backend.cache.key(request);
const cached = await env.CACHE_KV.get(cacheKey);
if (cached) {
return new Response(cached, {
headers: {
'Content-Type': 'application/json',
'X-Cache': 'HIT'
}
});
}
}
// バックエンドへのリクエスト
const backendUrl = `${backend.url}/${pathParts.join('/')}${url.search}`;
const backendRequest = new Request(backendUrl, {
method: request.method,
headers: request.headers,
body: request.body
});
try {
const response = await fetch(backendRequest, {
signal: AbortSignal.timeout(backend.timeout)
});
// レスポンスの加工
const data = await response.json();
const enrichedData = {
...data,
_metadata: {
service,
timestamp: new Date().toISOString(),
region: request.cf?.colo
}
};
const responseBody = JSON.stringify(enrichedData);
// キャッシュの保存
if (request.method === 'GET' && backend.cache && response.ok) {
ctx.waitUntil(
env.CACHE_KV.put(
backend.cache.key(request),
responseBody,
{ expirationTtl: backend.cache.ttl }
)
);
}
return new Response(responseBody, {
status: response.status,
headers: {
'Content-Type': 'application/json',
'X-Cache': 'MISS'
}
});
} catch (error) {
return new Response(
JSON.stringify({
error: 'Backend service unavailable',
service
}),
{ status: 503 }
);
}
}
};
// レート制限の実装
async function checkRateLimit(
request: Request,
kv: KVNamespace
): Promise<{ allowed: boolean; retryAfter: number }> {
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
const key = `rate_limit:${ip}`;
const limit = 100; // 1分あたり100リクエスト
const window = 60; // 60秒
const current = await kv.get(key);
const count = current ? parseInt(current) : 0;
if (count >= limit) {
return { allowed: false, retryAfter: window };
}
await kv.put(key, (count + 1).toString(), {
expirationTtl: window
});
return { allowed: true, retryAfter: 0 };
}
Edge Function の地理的分散と低レイテンシを活用して、リアルタイムパーソナライゼーションを実現できます。
// Vercel Edge Functions: 地域別コンテンツ配信
import { NextRequest, NextResponse } from 'next/server';
export async function middleware(request: NextRequest) {
const country = request.geo?.country || 'US';
const city = request.geo?.city || 'Unknown';
const language = request.headers.get('accept-language')?.split(',')[0] || 'en';
// 国別の設定
const countryConfig = {
JP: {
currency: 'JPY',
locale: 'ja-JP',
timezone: 'Asia/Tokyo',
features: ['qr-payment', 'line-login']
},
US: {
currency: 'USD',
locale: 'en-US',
timezone: 'America/New_York',
features: ['apple-pay', 'google-pay']
},
GB: {
currency: 'GBP',
locale: 'en-GB',
timezone: 'Europe/London',
features: ['open-banking']
}
};
const config = countryConfig[country] || countryConfig.US;
// レスポンスヘッダーに地域情報を追加
const response = NextResponse.next();
response.headers.set('X-User-Country', country);
response.headers.set('X-User-City', city);
response.headers.set('X-User-Currency', config.currency);
response.headers.set('X-User-Locale', config.locale);
// A/Bテストの割り当て
const abTestGroup = await assignABTestGroup(request);
response.headers.set('X-AB-Test-Group', abTestGroup);
// 地域制限のチェック
const restrictedCountries = ['CN', 'RU', 'KP'];
if (restrictedCountries.includes(country)) {
return NextResponse.redirect(new URL('/restricted', request.url));
}
return response;
}
// A/Bテストグループの割り当て
async function assignABTestGroup(request: NextRequest): Promise<string> {
const cookie = request.cookies.get('ab-test-group');
if (cookie) {
return cookie.value;
}
// ユーザーIDまたはIPアドレスでハッシュを生成
const userId = request.cookies.get('user-id')?.value ||
request.ip ||
'anonymous';
const encoder = new TextEncoder();
const data = encoder.encode(userId);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
// ハッシュ値に基づいてグループを決定(50/50分割)
const group = parseInt(hashHex.substring(0, 8), 16) % 2 === 0 ? 'A' : 'B';
return group;
}
export const config = {
matcher: ['/((?!api|_next/static|favicon.ico).*)']
};
戦略 | 説明 | ユースケース | 実装例 |
---|---|---|---|
エッジキャッシュ | CDNレベルでの静的アセットキャッシュ | イメージ、CSS、JS | Cache-Control: public, max-age=31536000 |
KVキャッシュ | Key-Valueストアでの動的データキャッシュ | APIレスポンス、セッション | env.KV.put(key, value, {expirationTtl: 300}) |
ブラウザキャッシュ | クライアントサイドキャッシュ | パーソナライズデータ | Cache-Control: private, max-age=3600 |
Stale-While-Revalidate | キャッシュ更新中も古いデータを配信 | ニュース、商品情報 | Cache-Control: s-maxage=60, stale-while-revalidate=300 |
// パフォーマンス最適化されたEdge Function
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// 1. 早期リターンの活用
const cached = await checkCache(request, env);
if (cached) return cached;
// 2. 並列処理の実装
const [userData, productData, recommendations] = await Promise.all([
fetchUserData(request, env),
fetchProductData(request, env),
fetchRecommendations(request, env)
]);
// 3. ストリーミングレスポンス
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
// ヘッダーを即座に送信
writer.write(new TextEncoder().encode('{"status":"ok","data":['));
// データを順次ストリーミング
for await (const chunk of processDataStream(userData, productData)) {
writer.write(new TextEncoder().encode(JSON.stringify(chunk) + ','));
}
writer.write(new TextEncoder().encode(']}'));
writer.close();
return new Response(readable, {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 's-maxage=10, stale-while-revalidate=60'
}
});
}
};
プロバイダー | 無料枠 | 追加コスト | 含まれるサービス |
---|---|---|---|
Cloudflare Workers | 100,000リクエスト/日 | $5/月 + $0.50/百万リクエスト | KV, Durable Objects, R2 |
Vercel Edge Functions | 100,000リクエスト/月 | $20/月 + $2/百万リクエスト | Analytics, Monitoring |
Netlify Edge Functions | 125,000リクエスト/月 | $19/月 + $2/百万リクエスト | Netlify Functions, Forms |
AWS Lambda@Edge | なし | $0.60/百万リクエスト + $0.00005125/GB-秒 | CloudFront統合 |
// 自動スケーリングとリソース管理
interface ScalingConfig {
maxConcurrency: number;
timeoutMs: number;
memoryMB: number;
}
export class EdgeFunctionScaler {
private config: ScalingConfig;
private metrics: MetricsCollector;
constructor(config: ScalingConfig) {
this.config = config;
this.metrics = new MetricsCollector();
}
async handleRequest(request: Request): Promise<Response> {
// リクエストの分類
const requestType = this.classifyRequest(request);
// 優先度に基づくリソース割り当て
switch (requestType) {
case 'critical':
return this.handleCritical(request);
case 'normal':
return this.handleNormal(request);
case 'batch':
return this.handleBatch(request);
default:
return new Response('Bad Request', { status: 400 });
}
}
private async handleCritical(request: Request): Promise<Response> {
// クリティカルなリクエストは即座に処理
const timeout = Math.min(this.config.timeoutMs, 5000);
return this.processWithTimeout(request, timeout);
}
private async handleBatch(request: Request): Promise<Response> {
// バッチ処理はキューに追加
const batchId = crypto.randomUUID();
await this.queueBatchJob(request, batchId);
return new Response(
JSON.stringify({
batchId,
status: 'queued',
estimatedTime: '30s'
}),
{ status: 202 }
);
}
}
完全な Edge-first アーキテクチャを採用した E コマースサイトの実装例を紹介します。
// Edge Function: グローバルEコマースゲートウェイ
interface EcommerceEnv {
PRODUCTS_KV: KVNamespace;
INVENTORY_API: string;
PAYMENT_API: string;
SHIPPING_API: string;
}
export default {
async fetch(
request: Request,
env: EcommerceEnv,
ctx: ExecutionContext
): Promise<Response> {
const url = new URL(request.url);
const router = new Router();
// 商品一覧(地域別価格対応)
router.get('/api/products', async (request) => {
const country = request.cf?.country || 'US';
const currency = getCurrency(country);
// KVから商品データを取得
const productsKey = `products:${country}`;
let products = await env.PRODUCTS_KV.get(productsKey);
if (!products) {
// オリジンから取得して地域別に最適化
const baseProducts = await fetchProductsFromOrigin(env);
products = await localizeProducts(
baseProducts,
country,
currency
);
// KVにキャッシュ(1時間)
ctx.waitUntil(
env.PRODUCTS_KV.put(productsKey, products, {
expirationTtl: 3600
})
);
}
return new Response(products, {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 's-maxage=300'
}
});
});
// 在庫確認(リアルタイム)
router.get('/api/inventory/:productId', async (request, { params }) => {
const { productId } = params;
const location = request.cf?.city || 'Unknown';
// 最寄りの倉庫から在庫を確認
const inventory = await checkInventory(
productId,
location,
env.INVENTORY_API
);
return new Response(
JSON.stringify({
productId,
available: inventory.quantity > 0,
quantity: inventory.quantity,
warehouse: inventory.warehouse,
estimatedDelivery: inventory.estimatedDelivery
}),
{
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache'
}
}
);
});
// チェックアウト処理
router.post('/api/checkout', async (request) => {
const order = await request.json();
const country = request.cf?.country || 'US';
// 並列処理で各種検証
const [
inventoryCheck,
fraudCheck,
shippingQuote,
taxCalculation
] = await Promise.all([
verifyInventory(order.items, env),
checkFraud(order, request),
calculateShipping(order, country, env),
calculateTax(order, country)
]);
if (!inventoryCheck.success) {
return new Response(
JSON.stringify({
error: 'Some items are out of stock',
items: inventoryCheck.unavailable
}),
{ status: 400 }
);
}
if (fraudCheck.risk > 0.8) {
return new Response(
JSON.stringify({
error: 'Transaction flagged for review'
}),
{ status: 403 }
);
}
// 支払い処理
const payment = await processPayment({
...order,
shipping: shippingQuote,
tax: taxCalculation,
currency: getCurrency(country)
}, env.PAYMENT_API);
if (payment.success) {
// 注文確定処理をバックグラウンドで
ctx.waitUntil(
finalizeOrder(order, payment, env)
);
return new Response(
JSON.stringify({
orderId: payment.orderId,
status: 'confirmed',
estimatedDelivery: shippingQuote.estimatedDate
}),
{ status: 200 }
);
}
return new Response(
JSON.stringify({
error: 'Payment failed',
reason: payment.error
}),
{ status: 400 }
);
});
return router.handle(request);
}
};
// 地域別通貨マッピング
function getCurrency(country: string): string {
const currencyMap: Record<string, string> = {
JP: 'JPY',
US: 'USD',
GB: 'GBP',
EU: 'EUR',
CN: 'CNY',
KR: 'KRW',
AU: 'AUD',
CA: 'CAD'
};
return currencyMap[country] || 'USD';
}
// 商品のローカライゼーション
async function localizeProducts(
products: any[],
country: string,
currency: string
): Promise<string> {
const exchangeRate = await getExchangeRate('USD', currency);
const localized = products.map(product => ({
...product,
price: {
amount: Math.round(product.price * exchangeRate),
currency,
formatted: formatPrice(
product.price * exchangeRate,
currency
)
},
availability: getLocalAvailability(product, country),
shipping: getShippingOptions(product, country)
}));
return JSON.stringify(localized);
}
チャートを読み込み中...
✅ セキュリティ
✅ パフォーマンス
✅ 可用性
✅ 監視とログ
✅ 開発プロセス
2025 年の Edge Function は、単なる CDN エッジでの処理を超えて、完全なアプリケーションプラットフォームへと進化しています。特に注目すべきトレンドは:
2025 年までに、全データの 75%がエッジで生成・処理されるようになり、Edge Function は現代の Web アーキテクチャにおいて不可欠な要素となる。
Edge Function を活用することで、グローバルスケールでの高速な Web アプリケーション構築が可能になります。適切なプラットフォーム選択と実装パターンの理解により、ユーザー体験の向上とコスト最適化を同時に実現できるでしょう。