Cloudflare Workers入門|エッジコンピューティングでAPIを高速化する方法

kento_morota 22分で読めます
社内のAI・IT活用、技術がわかる相手に無料相談。 相談 →
目次

「APIのレスポンスが遅くてユーザー体験が悪い」「海外からのアクセスに対応したいが、サーバーを各地に立てるのはコストが高い」——こうした課題に対して、Cloudflare Workersはエッジコンピューティングというアプローチで解決策を提供します。

Cloudflare Workersは、世界300以上のデータセンターでJavaScriptを実行できるサーバーレスプラットフォームです。ユーザーに最も近い場所でコードが実行されるため、従来のサーバーと比べて圧倒的に低いレイテンシを実現します。

本記事では、Cloudflare Workersの基本概念から実践的なAPI高速化の実装まで、段階的に解説します。

エッジコンピューティングとCloudflare Workersの基礎

エッジコンピューティングとは、処理をエンドユーザーに地理的に近い場所(エッジ)で実行する技術です。従来のクラウドコンピューティングでは、東京や米国のデータセンターにあるサーバーで処理を行っていました。エッジコンピューティングでは、世界中に分散されたエッジサーバーで処理を実行します。

Cloudflare Workersは、Cloudflareのグローバルネットワーク上でJavaScript(およびWebAssembly)を実行するプラットフォームです。V8エンジン(ChromeやNode.jsと同じJavaScriptエンジン)をベースにしており、コールドスタートが非常に高速です。

従来のサーバーレスとの違い

AWS Lambdaなどの従来のサーバーレスプラットフォームと比較したCloudflare Workersの特徴は以下の通りです。

コールドスタートの速さ
AWS Lambdaではコールドスタートに数百ms〜数秒かかることがありますが、Cloudflare WorkersはV8 Isolateモデルにより、コールドスタートが通常5ms以下です。

グローバル分散
従来のサーバーレスは特定のリージョン(例:ap-northeast-1)で実行されますが、Workersはデプロイするだけで世界300以上のロケーションに自動で分散されます。

Web標準API
Workersは、Service Workers APIやFetch APIなどのWeb標準に準拠しています。ブラウザのJavaScriptに近い書き方ができるため、フロントエンド開発者にとって馴染みやすいです。

料金体系

Freeプラン:1日10万リクエストまで無料、CPU時間10ms/リクエスト。個人プロジェクトやプロトタイプに十分です。

Standard プラン(月額5ドル〜):1,000万リクエスト/月含む、CPU時間30ms/リクエスト。超過分は従量課金です。

開発環境のセットアップ

Cloudflare Workersの開発には、Wranglerというコマンドラインツールを使用します。

プロジェクトの作成

# Wranglerのインストール
npm install -g wrangler

# Cloudflareアカウントへのログイン
wrangler login

# 新規プロジェクトの作成
npm create cloudflare@latest my-worker

# プロジェクト構成
my-worker/
├── src/
│   └── index.ts      # ワーカーのエントリーポイント
├── wrangler.toml      # 設定ファイル
├── package.json
└── tsconfig.json

基本的なWorkerの構造

// src/index.ts - 最もシンプルなWorker
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);

    // ルーティング
    if (url.pathname === '/api/hello') {
      return new Response(JSON.stringify({ message: 'Hello from the edge!' }), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

    if (url.pathname === '/api/time') {
      return new Response(JSON.stringify({
        timestamp: new Date().toISOString(),
        cf: request.cf, // Cloudflareが付加するリクエスト情報
      }), {
        headers: { 'Content-Type': 'application/json' },
      });
    }

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

interface Env {
  // 環境変数やバインディングの型定義
}
# ローカルでの開発サーバー起動
wrangler dev

# 本番へのデプロイ
wrangler deploy

wrangler devコマンドでローカル開発サーバーが起動し、ホットリロードで開発できます。デプロイはwrangler deployの一発で、世界中のエッジに配信されます。

実践ユースケース1:APIプロキシとキャッシュ

Cloudflare Workersの代表的なユースケースとして、外部APIのプロキシとエッジキャッシュの実装を見てみましょう。

外部APIのキャッシュプロキシ

外部APIのレスポンスをエッジでキャッシュし、同じリクエストに対してはキャッシュから高速に応答する仕組みです。

// src/index.ts - APIキャッシュプロキシ
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname.startsWith('/api/products')) {
      return handleProductsAPI(request, env, ctx);
    }

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

async function handleProductsAPI(
  request: Request,
  env: Env,
  ctx: ExecutionContext
): Promise<Response> {
  // Cache APIを使ったキャッシュ
  const cache = caches.default;
  const cacheKey = new Request(request.url, request);

  // キャッシュにヒットした場合はそのまま返す
  let response = await cache.match(cacheKey);
  if (response) {
    return new Response(response.body, {
      ...response,
      headers: {
        ...Object.fromEntries(response.headers),
        'X-Cache': 'HIT',
      },
    });
  }

  // キャッシュミスの場合、オリジンAPIにリクエスト
  const originResponse = await fetch('https://api.example.com/products', {
    headers: {
      'Authorization': `Bearer ${env.API_TOKEN}`,
    },
  });

  // レスポンスのクローンを作成(bodyは一度しか読めないため)
  response = new Response(originResponse.body, {
    status: originResponse.status,
    headers: {
      'Content-Type': 'application/json',
      'Cache-Control': 'public, max-age=300', // 5分キャッシュ
      'X-Cache': 'MISS',
    },
  });

  // バックグラウンドでキャッシュに保存
  ctx.waitUntil(cache.put(cacheKey, response.clone()));

  return response;
}

このパターンにより、オリジンAPIへのリクエスト数を大幅に削減しつつ、ユーザーにはエッジから高速にレスポンスを返せます。

実践ユースケース2:KV・D1・R2との連携

Cloudflareは、Workers向けのストレージサービスを複数提供しています。用途に応じて使い分けましょう。

Workers KV:キーバリューストア

KVは、グローバルに分散されたキーバリューストアです。読み取りが非常に高速(エッジでキャッシュ)で、設定情報やセッションデータの保存に適しています。

# KVネームスペースの作成
wrangler kv namespace create "MY_KV"
// wrangler.toml
[[kv_namespaces]]
binding = "MY_KV"
id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
// KVの読み書き
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (request.method === 'GET' && url.pathname === '/api/config') {
      // KVから読み取り
      const config = await env.MY_KV.get('site-config', 'json');
      return Response.json(config || { theme: 'default' });
    }

    if (request.method === 'PUT' && url.pathname === '/api/config') {
      const body = await request.json();
      // KVに書き込み(TTL: 1時間)
      await env.MY_KV.put('site-config', JSON.stringify(body), {
        expirationTtl: 3600,
      });
      return Response.json({ success: true });
    }

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

interface Env {
  MY_KV: KVNamespace;
}

D1:SQLiteデータベース

D1は、Cloudflareが提供するサーバーレスSQLiteデータベースです。SQLが使える本格的なリレーショナルデータベースをエッジで利用できます。

# D1データベースの作成
wrangler d1 create my-database

# マイグレーションの作成
wrangler d1 migrations create my-database init
-- migrations/0001_init.sql
CREATE TABLE articles (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  content TEXT NOT NULL,
  published_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_articles_slug ON articles(slug);
CREATE INDEX idx_articles_published ON articles(published_at);
// D1データベースの操作
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/api/articles') {
      const { results } = await env.DB.prepare(
        'SELECT id, title, slug, published_at FROM articles ORDER BY published_at DESC LIMIT 20'
      ).all();
      return Response.json({ articles: results });
    }

    if (url.pathname.startsWith('/api/articles/')) {
      const slug = url.pathname.split('/').pop();
      const article = await env.DB.prepare(
        'SELECT * FROM articles WHERE slug = ?'
      ).bind(slug).first();

      if (!article) {
        return Response.json({ error: 'Not found' }, { status: 404 });
      }
      return Response.json({ article });
    }

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

interface Env {
  DB: D1Database;
}

R2:オブジェクトストレージ

R2は、S3互換のオブジェクトストレージで、エグレス料金(データ転送料金)が無料という大きな特徴があります。画像やファイルの保存に最適です。

// R2へのファイルアップロード
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method === 'PUT' && request.url.includes('/upload/')) {
      const url = new URL(request.url);
      const key = url.pathname.replace('/upload/', '');
      const contentType = request.headers.get('Content-Type') || 'application/octet-stream';

      await env.MY_BUCKET.put(key, request.body, {
        httpMetadata: { contentType },
      });

      return Response.json({ key, url: `/files/${key}` });
    }

    if (request.method === 'GET' && request.url.includes('/files/')) {
      const url = new URL(request.url);
      const key = url.pathname.replace('/files/', '');
      const object = await env.MY_BUCKET.get(key);

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

      return new Response(object.body, {
        headers: {
          'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
          'Cache-Control': 'public, max-age=86400',
        },
      });
    }

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

interface Env {
  MY_BUCKET: R2Bucket;
}

実践ユースケース3:Honoフレームワークの活用

本格的なAPIをWorkersで構築する場合、Honoフレームワークの利用がおすすめです。HonoはCloudflare Workers向けに最適化された軽量Webフレームワークです。

npm create hono@latest my-api
// src/index.ts - Honoを使ったAPI
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { cache } from 'hono/cache';
import { jwt } from 'hono/jwt';

type Bindings = {
  DB: D1Database;
  MY_KV: KVNamespace;
  JWT_SECRET: string;
};

const app = new Hono<{ Bindings: Bindings }>();

// CORSミドルウェア
app.use('/api/*', cors({
  origin: ['https://example.com', 'https://staging.example.com'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
}));

// 公開API:キャッシュ付き
app.get('/api/articles',
  cache({ cacheName: 'articles', cacheControl: 'max-age=300' }),
  async (c) => {
    const { results } = await c.env.DB.prepare(
      'SELECT id, title, slug, published_at FROM articles ORDER BY published_at DESC LIMIT 20'
    ).all();
    return c.json({ articles: results });
  }
);

// 認証が必要なAPI
app.use('/api/admin/*', jwt({ secret: 'JWT_SECRET' }));

app.post('/api/admin/articles', async (c) => {
  const { title, slug, content } = await c.req.json();
  await c.env.DB.prepare(
    'INSERT INTO articles (title, slug, content) VALUES (?, ?, ?)'
  ).bind(title, slug, content).run();
  return c.json({ success: true }, 201);
});

export default app;

Honoを使うことで、ルーティング、ミドルウェア、バリデーション、認証などの機能を整理された形で実装できます。

デプロイと運用のベストプラクティス

Workersを本番運用する際のベストプラクティスを紹介します。

環境の分離

# wrangler.toml - 本番とステージングの分離
name = "my-api"
main = "src/index.ts"
compatibility_date = "2026-03-01"

# 本番環境の設定
[env.production]
vars = { ENVIRONMENT = "production" }
d1_databases = [
  { binding = "DB", database_name = "my-db-prod", database_id = "xxx" }
]

# ステージング環境の設定
[env.staging]
vars = { ENVIRONMENT = "staging" }
d1_databases = [
  { binding = "DB", database_name = "my-db-staging", database_id = "yyy" }
]
# 環境を指定してデプロイ
wrangler deploy --env production
wrangler deploy --env staging

エラーハンドリングとロギング

// グローバルエラーハンドリング
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    try {
      return await handleRequest(request, env, ctx);
    } catch (error) {
      // エラーログの送信(Logpushや外部サービスに送信)
      console.error('Unhandled error:', error);

      ctx.waitUntil(
        fetch('https://logging.example.com/errors', {
          method: 'POST',
          body: JSON.stringify({
            message: error instanceof Error ? error.message : 'Unknown error',
            url: request.url,
            timestamp: new Date().toISOString(),
          }),
        })
      );

      return Response.json(
        { error: 'Internal Server Error' },
        { status: 500 }
      );
    }
  },
};

レート制限の実装

// KVを使ったシンプルなレート制限
async function rateLimit(request: Request, env: Env): Promise<boolean> {
  const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
  const key = `rate:${ip}:${Math.floor(Date.now() / 60000)}`; // 1分単位

  const count = parseInt(await env.MY_KV.get(key) || '0');
  if (count >= 60) { // 1分あたり60リクエストまで
    return false;
  }

  await env.MY_KV.put(key, String(count + 1), { expirationTtl: 120 });
  return true;
}

まとめ

Cloudflare Workersは、エッジコンピューティングの力を手軽に活用できるプラットフォームです。本記事のポイントを振り返ります。

・Workersは世界300以上のロケーションでJavaScriptを実行し、低レイテンシを実現する
・コールドスタート5ms以下で、従来のサーバーレスより圧倒的に高速
・KV(キーバリュー)、D1(SQLite)、R2(オブジェクトストレージ)でデータを永続化できる
・Honoフレームワークを使えば、本格的なAPIを効率的に構築可能
・APIプロキシ・キャッシュ、画像最適化、A/Bテストなど多彩なユースケースに対応
・無料枠が充実しており、小規模プロジェクトならコストゼロで運用可能

まずはFreeプランでシンプルなAPIを作成し、エッジコンピューティングの速さを体感してみてください。wrangler devによるローカル開発とwrangler deployによるワンコマンドデプロイの体験は、開発者にとって非常に快適です。

#Cloudflare#エッジコンピューティング#Workers

Harmonic Society

この記事の内容、自社の業務でも活かせそうですか?

ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。

共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

関連記事

Related / 9 articles

  1. プログラミング

    DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用

    DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。

  2. プログラミング

    WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド

    WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。

  3. プログラミング

    セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説

    CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。

  4. プログラミング

    クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計

    クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。

  5. プログラミング

    秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除

    APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。

  6. プログラミング

    開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御

    開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。

  7. プログラミング

    ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方

    NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。

  8. プログラミング

    ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方

    ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。

  9. プログラミング

    localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決

    localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。

Harmonic Society

「読んで終わり」にせず、自社の業務で試してみませんか?

AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。

無料・30分・オンラインOK|1営業日以内に返信します