Contents
概要と導入判断(Cloudflare Workers 開発 手順)
Cloudflare Workers 開発 手順を求める開発者向けに、ローカル開発からバインディング、CI/CD、運用までの実務的な流れと注意点を整理します。短時間で試せるサンプルと運用チェックリストを示し、導入可否の判断基準も提示します。
- まず抑えるポイント
- 要件(レイテンシ/整合性/状態管理)と Workers の制約を突き合わせる
- 小さく作ってローカル→ステージング→本番の流れを確立する
- シークレットはリポジトリに入れない、CI 用に最小権限トークンを用意する
対象読者と推奨の読み方
初心者向けにはハンズオンを先に読むと効率的です。中級者はバインディングやCI/CD、運用チェックを中心に確認してください。上級者は耐障害性やコスト最適化、Durable Objects/D1 の運用設計を深掘りしてください。
初心者(まず触る)
最小APIをローカルで動かすことに注力してください。ハンズオンの「環境準備」「堅牢なサンプルコード」「ローカルテスト」を順に実行します。
中級者(運用に向けて)
binding の使い分け、ルーティング、CI の承認フロー、シークレット運用方針、ログ連携を整備してください。これらを読み、ステージング運用を確立します。
上級者(最適化と拡張)
Durable Objects や D1、R2 の性能・整合性特性を踏まえた設計、カナリア/段階的ロールアウト、コスト・メトリクス監視を実装します。
ハンズオン:最小の HTTP API を作る(Workers ハンズオン)
最小APIを作る流れを実務的に示します。ローカル起動、堅牢な入力処理、KVバインディング利用、ステージング経由での本番リリースまでをカバーします。
環境準備とバージョン前提
まず開発環境と利用するツールのバージョンを明示してください。ここでは wrangler v2.x 系を想定してコマンド例を示します。wrangler v1 と v2、また cloudflare/wrangler-action のバージョン差でコマンドや設定項目が異なるため、実運用前に利用バージョンを明記して差分を確認してください。
- 開発準備(例)
- wrangler のインストール(公式ガイドに従う)
- プロジェクト初期化:wrangler init sample-worker
- 依存関係:npm ci(package-lock.json がある場合)
- 型定義:npm i -D @cloudflare/workers-types
- テストツール(任意):npm i -D miniflare vitest
機密情報は wrangler secret put(ローカル)または CI の Secrets に設定し、リポジトリに含めないでください。
堅牢なサンプルコード(TypeScript 例)
ここでは入力検証、Content-Type チェック、ペイロード制限、エラーハンドリングを含めた実運用寄りの例を示します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
const CORS_HEADERS = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET,POST,OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type, Authorization', }; export default { async fetch(request: Request, env: any) { const url = new URL(request.url); // プリフライト対応 if (request.method === 'OPTIONS') { return new Response(null, { status: 204, headers: CORS_HEADERS }); } try { if (request.method === 'GET' && url.pathname === '/api/hello') { return new Response(JSON.stringify({ message: 'Hello from Workers' }), { headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } if (request.method === 'POST' && url.pathname === '/api/items') { const contentType = request.headers.get('Content-Type') || ''; if (!contentType.includes('application/json')) { return new Response(JSON.stringify({ error: 'Content-Type must be application/json' }), { status: 415, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } // ペイロードサイズの目安(1MB) const lengthHeader = request.headers.get('Content-Length'); if (lengthHeader && Number(lengthHeader) > 1_000_000) { return new Response(JSON.stringify({ error: 'Payload too large' }), { status: 413, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } const text = await request.clone().text(); if (text.length > 1_000_000) { return new Response(JSON.stringify({ error: 'Payload too large' }), { status: 413, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } let body: any; try { body = JSON.parse(text); } catch { return new Response(JSON.stringify({ error: 'Invalid JSON' }), { status: 400, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } // 簡単な入力検証(実運用ではスキーマバリデータ推奨) if (!body || typeof body.name !== 'string' || body.name.length === 0) { return new Response(JSON.stringify({ error: 'Invalid body: name is required' }), { status: 422, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } const id = body.id || crypto.randomUUID(); try { // KV への保存(エラーは 503 を返す) await env.MY_KV.put(`item:${id}`, JSON.stringify(body)); } catch (e) { return new Response(JSON.stringify({ error: 'Storage error' }), { status: 503, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } return new Response(JSON.stringify({ id }), { status: 201, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } return new Response('Not Found', { status: 404 }); } catch (err) { // 予期しないエラーのハンドリング。詳細はログに出す。 return new Response(JSON.stringify({ error: 'Internal server error' }), { status: 500, headers: { 'Content-Type': 'application/json', ...CORS_HEADERS }, }); } }, }; |
実運用では JSON スキーマバリデーション(AJV など)、署名付きリクエスト、レート制限を組み合わせてください。外部 API 呼び出しは AbortController でタイムアウト制御します。
ローカル起動・デバッグ・テスト
ここではローカル検証の手順を示します。wrangler dev と Miniflare の違いを理解して使い分けてください。
- wrangler dev でエッジ相当の挙動確認
- Miniflare はユニット/統合テスト(Durable Objects や R2 のエミュレーションに有用)
- ログ確認:wrangler dev のログ、wrangler tail(本番ストリーミング)
- テスト:Vitest/Jest と Miniflare を組み合わせた自動テスト
実行例(ローカル):
- GET: curl http://localhost:8787/api/hello
- POST: curl -X POST http://localhost:8787/api/items -d '{"name":"A"}' -H "Content-Type: application/json"
主要バインディングとルーティング(Workers のバインディング)
Workers と組み合わせる主要なバインディングの用途、制約、運用上の注意点を示します。設計段階で用途に応じて選択してください。
Workers KV
KV は小さな参照データやキャッシュ向けです。グローバルに分散して高速に読み出せますが、最終的一貫性(eventual consistency)に注意してください。
- 使い方(例):await env.MY_KV.get('key'); await env.MY_KV.put('key', value);
- 注意点:直後の読み取りで最新が見えない可能性があるため、整合性が必要なケースは他手法を検討
- コスト観点:大量読み書きの発生源になる箇所は監視が必要
Durable Objects
Durable Objects は特定キー単位で強い整合性と排他制御を提供します。セッション管理やカウンタ、ロックに向いていますが、実行場所やホットスポット、スケーリング、コストに注意が必要です。
- 特性:キーごとに単一インスタンスが割り当てられ、そのインスタンスで同期的に状態を管理
- 注意点:高負荷な単一オブジェクトはボトルネック化する。短時間の処理に限定し、分割・シャーディングを検討
- 利用例:env.COUNTER.idFromName('name') で id を取得し、stub.fetch(request) で呼び出す設計が一般的
R2(オブジェクトストレージ)
R2 は大きなファイルの保存やストリーミング配信に適しています。S3 互換性がありますが、保存・送信のコストとリージョンに伴うパフォーマンスを考慮してください。
- 使い方(例):const obj = await env.MY_R2.get('path'); return new Response(obj?.body || '', { headers: { 'Content-Type': obj?.httpMetadata?.contentType || 'application/octet-stream' } });
- 注意点:大きなファイルの配信はストリーミング設計を検討。データ転送量(egress)とキャッシュの組合せでコストが変わる
D1(SQL)
D1 は軽量なリレーショナルクエリに使えます。トランザクションやプリペアドステートメントに対応しますが、複雑クエリや大量データ処理は注意が必要です。
- 使い方(例):const res = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(id).all();
- 注意点:重い集計や長時間クエリはパフォーマンス課題につながるため、インデックス設計やクエリ分割を検討する
Secrets / 環境変数
シークレットは wrangler secret put や CI の Secrets を利用して運用します。コードやリポジトリに絶対に含めないことが最重要です。権限は最小にします。
ルーティングとキャッシュ設定
wrangler.toml の routes やダッシュボードでドメイン/サブドメインに Workers を割り当てます。パターンの優先度、ワイルドカード、キャッシュ制御(Cache-Control と Edge Cache TTL)を設計してください。
デプロイ(手動)と CI/CD 設定例
デプロイは安全を重視して自動化します。テスト→ビルド→承認→デプロイの流れを確立し、ロールバック手順を準備してください。
手動デプロイ(wrangler publish)
手動の基本手順です。事前チェックを確実に行います。
- ローカルで lint/test/build を実行して全て通す
- ステージングにデプロイ:wrangler publish --env staging
- スモークテスト(自動化されたエンドポイントテスト)を実行
- 問題なければ本番にデプロイ:wrangler publish --env production
デプロイ前に account_id / zone_id / binding の設定漏れがないか確認してください。
wrangler と wrangler-action のバージョン差分
利用するツールのバージョンを明記すると運用での混乱を避けられます。wrangler v1 と v2、cloudflare/wrangler-action の各バージョンでコマンドや設定項目が変わるため、以下を守ってください。
- 利用バージョンを README や CI ワークフローに明記する
- 主要な差分:config(wrangler.toml)のフィールドや env の扱い、action 入力パラメータに差が出ることがある
- 公式のリリースノートを参照し、CI のイメージや action を固定する
GitHub Actions ワークフロー例と運用ルール
自動デプロイ時は環境ごとにトークンを分け、承認ワークフローやブランチ保護を併用してください。以下は承認とロールバックを想定した骨子です。
- ワークフロー構成:checkout → setup-node → install → lint/test → build → deploy(環境ごとに secrets を分ける)
- production へのデプロイは GitHub の Environments(例:production)と連携し、必要レビューワーを設定する
- secrets はリポジトリの Secrets ではなく、環境シークレットや組織シークレットを使いアクセス制御を強化する
(補足)ロールバックはタグ運用やリリース履歴から旧コミットを再デプロイできるようにしておくことが重要です。CI に手動トリガの rollback ワークフローを用意すると短時間で復旧できます。
シークレット運用とローテーション
- CI 用トークンは最小権限で発行する
- トークンは環境(staging/production)ごとに分離する
- ローテーション頻度の目安は 90 日程度を推奨。疑わしい事象があれば即時ローテーションと旧トークンの無効化を行う
- 使っていないトークンは速やかに削除し、定期的に使用状況を監査する
運用・監視・セキュリティ・コスト最適化
本番運用でのチェックポイントとトラブル対応をまとめます。ログとメトリクスの収集、セキュリティ運用、コスト監視を実務的に構築してください。
運用監視(ログ/メトリクス/アラート)
運用ではログとメトリクスを組み合わせてアラートを設計します。
- ログ取得:wrangler tail でリアルタイム確認、長期分析は Logpush → S3/BigQuery 等に転送
- メトリクス:リクエスト数、エラー率、レイテンシ分布(p95/p99)を常時監視
- アラート設計:エラー率上昇、レイテンシ急増、コスト増加をトリガに通知する
外部 APM(Sentry/Datadog 等)は非同期送信でコストを抑えつつトレースを取得してください。
セキュリティと API トークン運用(実務)
API トークンの運用は実務で重要です。具体的な手順を整備してください。
- 最小権限で発行し、用途ごとに分割する(CI、手元、運用ツールなど)
- ローテーション手順:新トークン作成 → CI に反映 → スモークテスト → 旧トークン削除
- 監査:Cloudflare の監査ログや CI の監査でトークン使用を定期確認する
- グローバル API キーは可能な限り使わず、API トークンを利用する
入力検証と POST ハンドラの堅牢化
- Content-Type チェック、ペイロードサイズ制限、JSON スキーマ検証を実施する
- 外部呼び出しはタイムアウトで切る(AbortController)
- 返却するエラーは情報を絞り、内部例外はログにのみ出す
- Rate limiting は Cloudflare の WAF / Rate Limiting 機能と組み合わせる
パフォーマンス最適化とコスト管理
- キャッシュ戦略:Edge キャッシュ + KV を組み合わせてリクエストを削減する
- バンドル最適化:不要依存の除去、コード分割でファーストバイト改善
- 冷スタート対策:ウォームアップが可能なら短い定期実行で遅延を低減
- コスト監視:異常増加はアラート化し、原因(トラフィック、外部 API 呼び出し)を速やかに分析する
本番移行チェックリスト(抜粋)
- 自動テスト・静的解析が全て通っている
- シークレットが環境に安全に設定されている
- 監視・アラート・ログの転送が有効になっている
- スモークテストとロードテストを実施済み
- ロールバック手順が文書化され、動作確認済み
- ステークホルダー承認とブランチ保護ルールが設定済み
よくあるトラブルと対処法
- 認証エラー(401/403):API トークンの権限不足や account_id / zone_id の不整合を確認
- binding が undefined(env.MY_KV が undefined):wrangler.toml の binding 名とデプロイ先の namespace id を確認
- CORS エラー:OPTIONS を適切に処理し、Access-Control-Allow-* を返す
- レイテンシや 5xx 増加:外部 API の遅延、無限ループ、CPU 制限超過を疑いログで原因を特定
- キャッシュが効かない:Cache-Control ヘッダ、Cache API 利用、ルート設定を確認
ログはまず wrangler tail でリアルタイムに確認し、詳細は Logpush や外部ログストレージで分析してください。
参考リンク(公式を優先)
公式ドキュメント・製品ページを優先して参照してください。サードパーティのブログや記事は内容が古くなる可能性があるため、補助的に利用する際は公式で仕様を確認してください。
- Cloudflare Workers(公式ドキュメント/製品ページ)
- https://www.cloudflare.com/ja-jp/developer-platform/products/workers/
- Cloudflare 一般情報(公式)
- https://www.cloudflare.com/
- 公式ドキュメント内の各機能ページ(KV / Durable Objects / R2 / D1 / API Tokens / Logpush)は個別に参照してください
コミュニティ記事(Zenn/Qiita 等)は実践的なヒントになる一方で更新停止のリスクがあります。必ず公式ドキュメントと合わせて確認してください。
まとめ
Cloudflare Workers はエッジでの高速処理に適していますが、実行時間や整合性、バンドルサイズなどの制約を前提に設計する必要があります。ローカルで小さく検証し、堅牢な入力検証、CI の承認フロー、シークレットとトークン運用、ログ・アラートの整備を順に実施してください。まずはハンズオンで動く最小 API を作り、ステージング経由で運用要件に合致していることを確認してから本番移行する流れを推奨します。