cloudflare-worker-base について
cloudflare-worker-baseは、TypeScript、Hono、Viteを使う開発者向けのCloudflare Workers構築ガイドとテンプレート集です。静的アセット、APIルート、ローカル開発、デプロイを扱い、よくある設定上の問題を避ける方法も説明します。
主な機能
- 01Cloudflare Workerのプロジェクト作成手順
- 02Hono、Vite、Wranglerの設定
- 03APIルートと静的アセットの例
- 04ローカル開発、型生成、デプロイの案内
- 05ルーティングとビルドの一般的な問題を解説
活用シーン
- 01静的フロントエンド付きWorkerをテンプレートから作成
- 02アセットとの競合を避けてAPIルートを設定
- 03ローカルシミュレーションでテストしてWorkerをデプロイ
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ovachiever/ droid-tings --skill cloudflare-worker-baseインストール後、Claude Code で/cloudflare-worker-baseを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.md.claude-plugin/plugin.jsonreferences/architecture.mdcommon-issues.mddeployment.mdtemplates/package.jsontsconfig.jsonvite.config.tswrangler.jsonctemplates/public/index.htmlscript.jsstyles.csstemplates/src/index.ts詳細
Cloudflare Worker ベーススタック
本番環境でテスト済み:cloudflare-worker-base-test (https://cloudflare-worker-base-test.webfonts.workers.dev) 最終更新日:2025-10-20 ステータス:本番環境対応済み ✅
クイックスタート(5 分)
1. プロジェクトのひな形を作成
npm create cloudflare@latest my-worker -- \
--type hello-world \
--ts \
--git \
--deploy false \
--framework none
これらのフラグを指定する理由:
--type hello-world:シンプルな出発点--ts:TypeScript のサポート--git:Git リポジトリを初期化--deploy false:まだデプロイしない(先に設定)--framework none:Vite は自分で追加します
2. 依存関係をインストール
cd my-worker
npm install [email protected]
npm install -D @cloudflare/[email protected] vite@^7.0.0
バージョンに関する注記:
[email protected]:最新の安定版(2025-10-20 に確認済み)@cloudflare/[email protected]:最新の安定版。HMR の競合状態を修正vite:Cloudflare プラグインと互換性のある最新バージョン
3. Wrangler を設定
wrangler.jsonc を作成または更新します:
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-worker",
"main": "src/index.ts",
"account_id": "YOUR_ACCOUNT_ID", // Find this in your Cloudflare dashboard (Workers & Pages -> Overview).
"compatibility_date": "2025-10-11",
"observability": {
"enabled": true
},
"assets": {
"directory": "./public/",
"binding": "ASSETS",
"not_found_handling": "single-page-application",
"run_worker_first": ["/api/*"]
}
}
重要:run_worker_first の設定
- これがないと、SPA フォールバックが API ルートを横取りします
- API ルートが JSON ではなく
index.htmlを返します - 出典:workers-sdk #8879
4. Vite を設定
vite.config.ts を作成します:
import { defineConfig } from 'vite'
import { cloudflare } from '@cloudflare/vite-plugin'
export default defineConfig({
plugins: [
cloudflare({
// Optional: Configure the plugin if needed
}),
],
})
@cloudflare/vite-plugin を使う理由:
- Cloudflare 公式のプラグインです
- Workers での HMR をサポートします
- Miniflare を使ったローカル開発を可能にします
- バージョン 1.13.13 で「A hanging Promise was canceled」エラーが修正されています
4 つのステップでセットアップ
ステップ 1:API ルートを含む Hono アプリを作成
src/index.ts を作成します:
/**
* Cloudflare Worker with Hono
*
* CRITICAL: Export pattern to prevent build errors
* ✅ CORRECT: export default app
* ❌ WRONG: export default { fetch: app.fetch }
*/
import { Hono } from 'hono'
// Type-safe environment bindings
type Bindings = {
ASSETS: Fetcher
}
const app = new Hono<{ Bindings: Bindings }>()
/**
* API Routes
* Handled BEFORE static assets due to run_worker_first config
*/
app.get('/api/hello', (c) => {
return c.json({
message: 'Hello from Cloudflare Workers!',
timestamp: new Date().toISOString(),
})
})
app.get('/api/health', (c) => {
return c.json({
status: 'ok',
version: '1.0.0',
environment: c.env ? 'production' : 'development',
})
})
/**
* Fallback to Static Assets
* Any route not matched above is served from public/ directory
*/
app.all('*', (c) => {
return c.env.ASSETS.fetch(c.req.raw)
})
/**
* Export the Hono app directly (ES Module format)
* This is the correct pattern for Cloudflare Workers with Hono + Vite
*/
export default app
このエクスポート形式を使う理由:
- 出典:honojs/hono #3955
{ fetch: app.fetch }を使うと、「Cannot read properties of undefined (reading 'map)」が発生します- 例外:scheduled/tail ハンドラーが必要な場合は、Module Worker 形式を使います:
export default { fetch: app.fetch, scheduled: async (event, env, ctx) => { /* ... */ } }
ステップ 2:静的フロントエンドを作成
public/index.html を作成します:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Worker App</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<div class="container">
<h1>Cloudflare Worker + Static Assets</h1>
<button onclick="testAPI()">Test API</button>
<pre id="output"></pre>
</div>
<script src="/script.js"></script>
</body>
</html>
public/script.js を作成します:
async function testAPI() {
const response = await fetch('/api/hello')
const data = await response.json()
document.getElementById('output').textContent = JSON.stringify(data, null, 2)
}
public/styles.css を作成します:
body {
font-family: system-ui, -apple-system, sans-serif;
max-width: 800px;
margin: 40px auto;
padding: 20px;
}
button {
background: #0070f3;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
}
pre {
background: #f5f5f5;
padding: 16px;
border-radius: 6px;
overflow-x: auto;
}
ステップ 3:パッケージスクリプトを更新
package.json を更新します:
{
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"cf-typegen": "wrangler types"
}
}
ステップ 4:テストしてデプロイ
# Generate TypeScript types for bindings
npm run cf-typegen
# Start local dev server (http://localhost:8787)
npm run dev
# Deploy to production
npm run deploy
既知の問題を防ぐ
このスキルでは、文書化されている 6 つの問題を防ぎます:
問題 #1:エクスポート構文エラー
エラー:「Cannot read properties of undefined (reading 'map')」
出典:honojs/hono #3955
対策:export default app を使います({ fetch: app.fetch } は使わないでください)
問題 #2:静的アセットのルーティング競合
エラー:API ルートが JSON ではなく index.html を返す
出典:workers-sdk #8879
対策:wrangler.jsonc に "run_worker_first": ["/api/*"] を追加します
問題 #3:Scheduled/Cron がエクスポートされない
エラー:「Handler does not export a scheduled() function」 出典:honojs/vite-plugins #275 対策:必要な場合は Module Worker 形式を使います:
export default {
fetch: app.fetch,
scheduled: async (event, env, ctx) => { /* ... */ }
}
問題 #4:HMR の競合状態
エラー:開発中に「A hanging Promise was canceled」が発生する
出典:workers-sdk #9518
対策:@cloudflare/[email protected] 以降を使います
問題 #5:静的アセットのアップロード競合
エラー:CI/CD でデプロイが予測不能な形で失敗する 出典:workers-sdk #7555 対策:再試行ロジックを備えた Wrangler 4.x 以降を使います(最近のバージョンで修正済み)
問題 #6:Service Worker 形式の混同
エラー:非推奨の Service Worker 形式を使用する 出典:Cloudflare の移行ガイド 対策:常に ES Module 形式を使います(ステップ 1 を参照)
設定ファイルのリファレンス
wrangler.jsonc(完全な例)
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-worker",
"main": "src/index.ts",
"account_id": "YOUR_ACCOUNT_ID", // Find this in your Cloudflare dashboard (Workers & Pages -> Overview).
"compatibility_date": "2025-10-11",
"observability": {
"enabled": true
},
"assets": {
"directory": "./public/",
"binding": "ASSETS",
"not_found_handling": "single-page-application",
"run_worker_first": ["/api/*"]
}
/* Optional: Environment Variables */
// "vars": { "MY_VARIABLE": "production_value" }
/* Optional: KV Namespace Bindings */
// "kv_namespaces": [
// { "binding": "MY_KV", "id": "YOUR_KV_ID" }
// ]
/* Optional: D1 Database Bindings */
// "d1_databases": [
// { "binding": "DB", "database_name": "my-db", "database_id": "YOUR_DB_ID" }
// ]
/* Optional: R2 Bucket Bindings */
// "r2_buckets": [
// { "binding": "MY_BUCKET", "bucket_name": "my-bucket" }
// ]
}
wrangler.toml ではなく wrangler.jsonc を使う理由:
- Wrangler v3.91.0 以降は JSON 形式が推奨されています
- JSON スキーマにより IDE のサポートが向上します
- JSONC ではコメントを使用できます
vite.config.ts(完全な例)
import { defineConfig } from 'vite'
import { cloudflare } from '@cloudflare/vite-plugin'
export default defineConfig({
plugins: [
cloudflare({
// Persist state between HMR updates
persist: true,
}),
],
// Optional: Configure server
server: {
port: 8787,
},
// Optional: Build optimizations
build: {
target: 'esnext',
minify: true,
},
})
tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"lib": ["ES2022"],
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types/2023-07-01"],
"resolveJsonModule": true,
"allowJs": true,
"checkJs": false,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"noEmit": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
API ルートのパターン
基本的な JSON レスポンス
app.get('/api/users', (c) => {
return c.json({
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
]
})
})
リクエストボディを使った POST
app.post('/api/users', async (c) => {
const body = await c.req.json()
// Validate and process body
return c.json({ success: true, data: body }, 201)
})
ルートパラメーター
app.get('/api/users/:id', (c) => {
const id = c.req.param('id')
return c.json({ id, name: 'User' })
})
クエリパラメーター
app.get('/api/search', (c) => {
const query = c.req.query('q')
return c.json({ query, results: [] })
})
エラー処理
app.get('/api/data', async (c) => {
try {
// Your logic here
return c.json({ success: true })
} catch (error) {
return c.json({ error: error.message }, 500)
}
})
バインディング(KV、D1、R2)の使用
type Bindings = {
ASSETS: Fetcher
MY_KV: KVNamespace
DB: D1Database
MY_BUCKET: R2Bucket
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/api/data', async (c) => {
// KV
const value = await c.env.MY_KV.get('key')
// D1
const result = await c.env.DB.prepare('SELECT * FROM users').all()
// R2
const object = await c.env.MY_BUCKET.get('file.txt')
return c.json({ value, result, object })
})
静的アセットのベストプラクティス
ディレクトリ構成
public/
├── index.html # Main entry point
├── styles.css # Global styles
├── script.js # Client-side JavaScript
├── favicon.ico # Favicon
└── assets/ # Images, fonts, etc.
├── logo.png
└── fonts/
SPA フォールバック
"not_found_handling": "single-page-application" の設定には、次の意味があります。
- 不明なルートは
index.htmlを返します - React Router、Vue Router などで役立ちます
- ただし、API ルートには
run_worker_firstが必要です!
ルートの優先順位
"run_worker_first": ["/api/*"] の場合:
/api/hello→ Worker が処理します(JSON を返します)/→ 静的アセットがindex.htmlを配信します/styles.css→ 静的アセットがstyles.cssを配信します/unknown→ 静的アセットがindex.htmlを配信します(SPA フォールバック)
静的アセットのキャッシュ
静的アセットはエッジで自動的にキャッシュされます。キャッシュを無効化するには:
<link rel="stylesheet" href="/styles.css?v=1.0.0">
<script src="/script.js?v=1.0.0"></script>
開発ワークフロー
ローカル開発
npm run dev
- サーバーは http://localhost:8787 で実行されます
- HMR が有効です(ファイルが変更されると自動的に再読み込みされます)
- ローカルシミュレーションには Miniflare を使用します
- すべてのバインディングをローカルで使用できます(KV、D1、R2)
API ルートのテスト
# Test GET endpoint
curl http://localhost:8787/api/hello
# Test POST endpoint
curl -X POST http://localhost:8787/api/echo \
-H "Content-Type: application/json" \
-d '{"test": "data"}'
型の生成
npm run cf-typegen
worker-configuration.d.ts を生成します。生成されるファイルには次の内容が含まれます。
- バインディングの型(KV、D1、R2 など)
- 環境変数の型
- エディターでの自動補完
デプロイ
# Deploy to production
npm run deploy
# Deploy to specific environment
wrangler deploy --env staging
# Tail logs in production
wrangler tail
# Check deployment status
wrangler deployments list
セットアップの完全チェックリスト
-
npm create cloudflare@latestでプロジェクトを作成した - 依存関係をインストールした:
[email protected]、@cloudflare/[email protected] -
wrangler.jsoncを作成し、次の内容を設定した:-
account_idに Cloudflare アカウントを設定した -
assets.directoryが./public/を指すように設定した -
assets.run_worker_firstに/api/*を含めた -
compatibility_dateに直近の日付を設定した
-
-
@cloudflare/vite-pluginを使用してvite.config.tsを作成した - Hono アプリを含む
src/index.tsを作成した-
export default appを使用している({ fetch: app.fetch }ではありません) - ASSETS バインディングの型を含めている
- フォールバックルートがある:
app.all('*', (c) => c.env.ASSETS.fetch(c.req.raw))
-
- 静的ファイルを含む
public/ディレクトリを作成した -
npm run cf-typegenが正常に実行された -
npm run devがエラーなしで起動する - API ルートをブラウザー/curl でテストした
- 静的アセットが正しく配信される
- クラッシュせずに HMR が動作する
-
npm run deployでデプロイできる状態になっている
高度なトピック
ミドルウェアの追加
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
const app = new Hono<{ Bindings: Bindings }>()
// Global middleware
app.use('*', logger())
app.use('/api/*', cors())
// Route-specific middleware
app.use('/admin/*', async (c, next) => {
// Auth check
await next()
})
環境ごとの設定
// wrangler.jsonc
{
"name": "my-worker",
"env": {
"staging": {
"vars": { "ENV": "staging" }
},
"production": {
"vars": { "ENV": "production" }
}
}
}
デプロイ:wrangler deploy --env staging
カスタムエラーページ
app.onError((err, c) => {
console.error(err)
return c.json({ error: 'Internal Server Error' }, 500)
})
app.notFound((c) => {
return c.json({ error: 'Not Found' }, 404)
})
Vitest を使用したテスト
npm install -D vitest @cloudflare/vitest-pool-workers
vitest.config.ts を作成します。
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.jsonc' },
},
},
},
})
テストガイドの全編については、reference/testing.md を参照してください。
ファイルテンプレート
すべてのテンプレートは templates/ ディレクトリにあります。
- wrangler.jsonc - Worker の完全な設定
- vite.config.ts - Vite と Cloudflare プラグインのセットアップ
- package.json - 依存関係とスクリプト
- tsconfig.json - TypeScript の設定
- src/index.ts - API ルートを含む Hono アプリ
- public/index.html - 静的フロントエンドの例
- public/styles.css - スタイル設定の例
- public/script.js - API テスト関数
これらのファイルをプロジェクトにコピーし、必要に応じてカスタマイズしてください。
リファレンスドキュメント
詳しくは、次のドキュメントを参照してください。
- architecture.md - エクスポートパターン、ルーティング、静的アセットの詳細
- common-issues.md - 6 つの問題すべてと詳細なトラブルシューティング
- deployment.md - Wrangler コマンド、CI/CD パターン、本番環境でのヒント
公式ドキュメント
- Cloudflare Workers:https://developers.cloudflare.com/workers/
- 静的アセット:https://developers.cloudflare.com/workers/static-assets/
- Vite プラグイン:https://developers.cloudflare.com/workers/vite-plugin/
- Wrangler の設定:https://developers.cloudflare.com/workers/wrangler/configuration/
- Hono:https://hono.dev/docs/getting-started/cloudflare-workers
- Context7 ライブラリ ID:
/websites/developers_cloudflare-workers
依存関係(2025-10-20 に最新を確認)
{
"dependencies": {
"hono": "^4.10.1"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.13.13",
"@cloudflare/workers-types": "^4.20251011.0",
"vite": "^7.0.0",
"wrangler": "^4.43.0",
"typescript": "^5.9.0"
}
}
本番環境での実例
このスキルは、cloudflare-worker-base-test プロジェクトに基づいています。
- 稼働中: https://cloudflare-worker-base-test.webfonts.workers.dev
- ビルド時間: 約45分(実測)
- エラー: 0件(既知の6つの問題をすべて防止)
- 検証: ✅ ローカル開発、HMR、本番環境へのデプロイがすべて成功
このスキルのすべてのパターンは、本番環境で検証済みです。
ご質問や問題がありますか?
- まず
reference/common-issues.mdを確認してください - 4ステップのセットアップ手順をすべて確認してください
export default appを使用していることを確認してください({ fetch: app.fetch }ではありません)run_worker_firstが設定されていることを確認してください- 公式ドキュメントを確認してください:https://developers.cloudflare.com/workers/