Edge Functions - エッジコンピューティングの実践

15分 で読める | 2025.01.21

Edge Functionsとは

Edge Functionsは、ユーザーに最も近いエッジロケーション(CDNの各拠点)でコードを実行するサーバーレス関数です。従来のサーバーレス(AWS Lambdaなど)と比較して、圧倒的に低いレイテンシーを実現します。

flowchart LR
    subgraph Traditional["従来のサーバーレス"]
        User1["ユーザー<br/>(東京)"] --> Region["リージョン<br/>(バージニア)"]
        Region --> Response1["応答<br/>200ms"]
    end

    subgraph Edge["Edge Functions"]
        User2["ユーザー<br/>(東京)"] --> EdgeLoc["エッジ<br/>(東京)"]
        EdgeLoc --> Response2["応答<br/>20ms"]
    end

主要プラットフォーム

1. Cloudflare Workers

最も成熟したエッジプラットフォームの一つです。

// Cloudflare Worker
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // キャッシュチェック
    const cache = caches.default;
    let response = await cache.match(request);

    if (!response) {
      // オリジンからフェッチ
      response = await fetch(request);

      // キャッシュに保存
      const responseToCache = response.clone();
      responseToCache.headers.set('Cache-Control', 'max-age=3600');
      await cache.put(request, responseToCache);
    }

    return response;
  },
};

2. Vercel Edge Functions

Next.jsと深く統合されたエッジ実行環境です。

// middleware.ts (Next.js)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 地理情報に基づくルーティング
  const country = request.geo?.country || 'US';

  if (country === 'JP') {
    return NextResponse.rewrite(new URL('/jp', request.url));
  }

  // A/Bテスト
  const bucket = Math.random() < 0.5 ? 'a' : 'b';
  const response = NextResponse.next();
  response.cookies.set('ab-test', bucket);

  return response;
}

export const config = {
  matcher: ['/((?!api|_next/static|favicon.ico).*)'],
};

3. Deno Deploy

Deno ランタイムベースのエッジプラットフォームです。

// main.ts (Deno Deploy)
Deno.serve(async (req: Request) => {
  const url = new URL(req.url);

  if (url.pathname === '/api/data') {
    // KVストアからデータ取得
    const kv = await Deno.openKv();
    const data = await kv.get(['cache', 'data']);

    if (data.value) {
      return Response.json(data.value);
    }

    // データを生成してキャッシュ
    const newData = { timestamp: Date.now(), message: 'Hello from Edge!' };
    await kv.set(['cache', 'data'], newData, { expireIn: 60000 });

    return Response.json(newData);
  }

  return new Response('Not Found', { status: 404 });
});

4. AWS Lambda@Edge / CloudFront Functions

AWSのCDNと統合されたエッジ実行環境です。

// CloudFront Function
function handler(event) {
  const request = event.request;
  const headers = request.headers;

  // デバイス検出
  const userAgent = headers['user-agent']?.value || '';
  const isMobile = /Mobile|Android/i.test(userAgent);

  // モバイル用URLにリダイレクト
  if (isMobile && !request.uri.startsWith('/m/')) {
    return {
      statusCode: 302,
      headers: {
        location: { value: `/m${request.uri}` },
      },
    };
  }

  return request;
}

ユースケース

1. 認証・認可

// JWTトークン検証をエッジで実行
async function verifyToken(request: Request): Promise<Response | null> {
  const authHeader = request.headers.get('Authorization');

  if (!authHeader?.startsWith('Bearer ')) {
    return new Response('Unauthorized', { status: 401 });
  }

  const token = authHeader.slice(7);

  try {
    const payload = await verifyJWT(token, JWT_SECRET);

    // ユーザー情報をヘッダーに追加してオリジンへ転送
    const newRequest = new Request(request, {
      headers: new Headers(request.headers),
    });
    newRequest.headers.set('X-User-ID', payload.sub);

    return null; // 続行
  } catch {
    return new Response('Invalid Token', { status: 401 });
  }
}

2. パーソナライゼーション

// 地理・言語に基づくコンテンツ最適化
export default {
  async fetch(request: Request): Promise<Response> {
    const country = request.cf?.country || 'US';
    const language = request.headers.get('Accept-Language')?.split(',')[0] || 'en';

    // 地域別価格表示
    const prices = {
      US: { currency: 'USD', amount: 9.99 },
      JP: { currency: 'JPY', amount: 980 },
      EU: { currency: 'EUR', amount: 8.99 },
    };

    const price = prices[country] || prices['US'];

    // HTMLをエッジで生成
    const html = `
      <!DOCTYPE html>
      <html lang="${language}">
      <body>
        <h1>Welcome!</h1>
        <p>Price: ${price.currency} ${price.amount}</p>
      </body>
      </html>
    `;

    return new Response(html, {
      headers: { 'Content-Type': 'text/html' },
    });
  },
};

3. APIゲートウェイ

// レート制限・ルーティング
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const ip = request.headers.get('CF-Connecting-IP') || 'unknown';

    // レート制限(Cloudflare Durable Objects使用)
    const rateLimiter = env.RATE_LIMITER.get(env.RATE_LIMITER.idFromName(ip));
    const { allowed } = await rateLimiter.fetch(request).then(r => r.json());

    if (!allowed) {
      return new Response('Too Many Requests', { status: 429 });
    }

    // パスベースルーティング
    const url = new URL(request.url);
    const routes = {
      '/api/users': 'https://users-service.internal',
      '/api/products': 'https://products-service.internal',
      '/api/orders': 'https://orders-service.internal',
    };

    for (const [path, origin] of Object.entries(routes)) {
      if (url.pathname.startsWith(path)) {
        return fetch(new URL(url.pathname, origin), request);
      }
    }

    return new Response('Not Found', { status: 404 });
  },
};

4. 画像最適化

// 画像のリサイズ・フォーマット変換
export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    const imageUrl = url.searchParams.get('url');
    const width = parseInt(url.searchParams.get('w') || '800');
    const format = url.searchParams.get('f') || 'webp';

    if (!imageUrl) {
      return new Response('Missing image URL', { status: 400 });
    }

    // Cloudflare Image Resizing
    return fetch(imageUrl, {
      cf: {
        image: {
          width,
          format,
          quality: 80,
        },
      },
    });
  },
};

制約と最適化

実行時間制限

プラットフォームCPU時間実時間
Cloudflare Workers10ms〜50ms30秒
Vercel Edge25秒25秒
Deno Deploy50ms無制限
Lambda@Edge5秒30秒

コールドスタート

// コールドスタート最小化のベストプラクティス
// 1. グローバルスコープで初期化を避ける
// Bad
const heavyConfig = loadHeavyConfig(); // 毎回実行される

// Good
let cachedConfig: Config | null = null;
function getConfig(): Config {
  if (!cachedConfig) {
    cachedConfig = loadConfig();
  }
  return cachedConfig;
}

メモリ制限

// ストリーミング処理でメモリを節約
export default {
  async fetch(request: Request): Promise<Response> {
    const response = await fetch('https://api.example.com/large-data');

    // ストリーム変換
    const transformStream = new TransformStream({
      transform(chunk, controller) {
        // チャンクごとに処理
        const processed = processChunk(chunk);
        controller.enqueue(processed);
      },
    });

    return new Response(response.body?.pipeThrough(transformStream), {
      headers: response.headers,
    });
  },
};

プラットフォーム比較

特徴Cloudflare WorkersVercel EdgeDeno Deploy
ランタイムV8 IsolatesV8 IsolatesDeno
グローバル拠点300+100+35+
KVストア◎ Workers KV○ Edge Config◎ Deno KV
データベース◎ D1 (SQLite)○ Postgres○ KV
無料枠100k req/日100k req/月100k req/日

関連記事

まとめ

Edge Functionsは、グローバルに低レイテンシーなアプリケーションを構築するための強力なツールです。

  • 低レイテンシー: ユーザーに最も近い場所で実行
  • グローバル配信: 自動的に世界中に展開
  • コスト効率: 使用量ベースの課金
  • シンプルなデプロイ: git pushで即座に反映

認証、パーソナライゼーション、APIゲートウェイなど、適切なユースケースを選んで活用しましょう。

この技術を体系的に学びたいですか?

未来学では東証プライム上場企業のITエンジニアが24時間サポート。月額24,800円から、退会金0円のオンラインIT塾です。

LINEで無料相談する
← 一覧に戻る