Hono × Cloudflare Workers完全ガイド — エッジで動く高速APIをサーバーレスで構築する方法【2026年版】
HonoはCloudflare Workersのために生まれたフレームワーク。世界300+都市のエッジでコールドスタート5ms以下・ゼロ運用コストのAPIを構築。D1・KV・R2・Workers AIとの統合からデプロイまで徹底解説。
HonoとCloudflare Workersの相性 — エッジで最速のAPIを実現
HonoはCloudflare Workersのために生まれたフレームワークです。世界300以上の都市に展開するCloudflareのエッジネットワーク上でネイティブに実行され、コールドスタート5ms以下・ゼロ運用コストのAPIを実現します。従来のサーバーベースAPIと比較して、インフラ管理が不要でグローバルに低レイテンシを達成できます。
環境構築手順 — プロジェクト作成からwranglerセットアップまで
# プロジェクト作成(cloudflare-workersテンプレートを選択)
npm create hono@latest my-edge-api
# テンプレート選択: cloudflare-workers
cd my-edge-api
npm install
# Wrangler CLIのグローバルインストール(未導入の場合)
npm install -g wrangler
# Cloudflareアカウントへのログイン
wrangler login
# ローカル開発サーバー起動
npm run dev
# => http://localhost:8787wrangler.toml設定例 — Bindingsの定義
# wrangler.toml
name = "my-edge-api"
main = "src/index.ts"
compatibility_date = "2024-01-01"
# KV Namespaceのバインディング
[[kv_namespaces]]
binding = "CACHE"
id = "your-kv-namespace-id"
# D1 Databaseのバインディング
[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-d1-database-id"
# R2 Bucketのバインディング
[[r2_buckets]]
binding = "STORAGE"
bucket_name = "my-bucket"
# Workers AIのバインディング
[ai]
binding = "AI"
# 環境変数
[vars]
ENVIRONMENT = "production"Bindingsの型定義 — TypeScriptで型安全な実装
// src/index.ts
import { Hono } from 'hono'
// Bindingsの型定義
type Bindings = {
DB: D1Database
CACHE: KVNamespace
STORAGE: R2Bucket
AI: Ai
ENVIRONMENT: string
}
// 型付きHonoインスタンス
const app = new Hono<{ Bindings: Bindings }>()
app.get('/', (c) => {
// c.env.DBなどに型補完が効く
const env = c.env.ENVIRONMENT
return c.json({ env, message: 'Hello from the edge!' })
})
export default appD1(SQLiteデータベース)連携 — CRUD操作の完全実装
// D1 CRUD操作の実装例
app.get('/api/users', async (c) => {
const result = await c.env.DB
.prepare('SELECT * FROM users LIMIT 100')
.all()
return c.json(result.results)
})
app.get('/api/users/:id', async (c) => {
const id = c.req.param('id')
const user = await c.env.DB
.prepare('SELECT * FROM users WHERE id = ?')
.bind(id)
.first()
if (!user) return c.json({ error: 'Not found' }, 404)
return c.json(user)
})
app.post('/api/users', async (c) => {
const { name, email } = await c.req.json()
const result = await c.env.DB
.prepare('INSERT INTO users (name, email) VALUES (?, ?)')
.bind(name, email)
.run()
return c.json({ id: result.meta.last_row_id }, 201)
})
app.delete('/api/users/:id', async (c) => {
const id = c.req.param('id')
await c.env.DB
.prepare('DELETE FROM users WHERE id = ?')
.bind(id)
.run()
return c.json({ deleted: true })
})KV(Key-Value)連携 — キャッシュ実装
// KVを使ったキャッシュ実装
app.get('/api/products/:id', async (c) => {
const id = c.req.param('id')
const cacheKey = `product:${id}`
// キャッシュから取得
const cached = await c.env.CACHE.get(cacheKey, 'json')
if (cached) {
c.header('X-Cache', 'HIT')
return c.json(cached)
}
// DBから取得してキャッシュに保存
const product = await c.env.DB
.prepare('SELECT * FROM products WHERE id = ?')
.bind(id)
.first()
if (!product) return c.json({ error: 'Not found' }, 404)
// TTL 3600秒でキャッシュ
await c.env.CACHE.put(cacheKey, JSON.stringify(product), {
expirationTtl: 3600
})
c.header('X-Cache', 'MISS')
return c.json(product)
})R2(オブジェクトストレージ)連携 — ファイルアップロード/ダウンロード
// R2ファイルアップロード
app.put('/api/files/:filename', async (c) => {
const filename = c.req.param('filename')
const body = await c.req.arrayBuffer()
const contentType = c.req.header('content-type') ?? 'application/octet-stream'
await c.env.STORAGE.put(filename, body, {
httpMetadata: { contentType }
})
return c.json({ uploaded: filename })
})
// R2ファイルダウンロード
app.get('/api/files/:filename', async (c) => {
const filename = c.req.param('filename')
const object = await c.env.STORAGE.get(filename)
if (!object) return c.json({ error: 'Not found' }, 404)
const headers = new Headers()
object.writeHttpMetadata(headers)
headers.set('etag', object.httpEtag)
return c.body(object.body, { headers })
})Workers AI連携 — LLM・画像生成・埋め込みベクトル
// LLMテキスト生成
app.post('/api/ai/chat', async (c) => {
const { message } = await c.req.json()
const response = await c.env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
messages: [{ role: 'user', content: message }]
})
return c.json({ reply: response.response })
})
// テキスト埋め込み(RAGなど)
app.post('/api/ai/embed', async (c) => {
const { text } = await c.req.json()
const embeddings = await c.env.AI.run('@cf/baai/bge-small-en-v1.5', {
text: [text]
})
return c.json({ vector: embeddings.data[0] })
})Cron Triggers — 定期実行ジョブの実装
// src/index.ts
import { Hono } from 'hono'
type Bindings = { DB: D1Database }
const app = new Hono<{ Bindings: Bindings }>()
// 通常のHTTPハンドラ
app.get('/', (c) => c.text('API is running'))
// Cron Triggerハンドラ(scheduled export)
export default {
fetch: app.fetch,
async scheduled(event: ScheduledEvent, env: Bindings) {
console.log('Cron triggered:', event.cron)
// 毎日0時に古いデータを削除
if (event.cron === '0 0 * * *') {
await env.DB
.prepare('DELETE FROM logs WHERE created_at < datetime("now", "-30 days")')
.run()
}
}
}`wrangler.toml` にCronスケジュールを追加:
[[triggers.crons]]
crons = ["0 0 * * *"]デプロイ手順 — 本番環境への反映
# 本番デプロイ
npx wrangler deploy
# 特定環境へのデプロイ
npx wrangler deploy --env production
# 独自ドメインの設定(wrangler.tomlに追記)
# [[routes]]
# pattern = "api.example.com/*"
# zone_name = "example.com"
# デプロイ後の確認
npx wrangler tail # リアルタイムログ確認ローカル開発 — リモートモードとローカルモード
# ローカルモード(ローカルのD1・KVシミュレーター使用)
npx wrangler dev
# リモートモード(本番のD1・KVに接続して開発)
npx wrangler dev --remote
# ポートを指定
npx wrangler dev --port 9000D1データベースをローカル環境で初期化する場合:
# マイグレーションファイルの作成
npx wrangler d1 migrations create my-database create-users
# ローカルD1への適用
npx wrangler d1 migrations apply my-database --local
# 本番D1への適用
npx wrangler d1 migrations apply my-databaseシークレット管理 — wrangler secret put の使い方
# シークレットの設定(プロンプトで値を入力)
npx wrangler secret put JWT_SECRET
npx wrangler secret put DATABASE_URL
# シークレット一覧の確認
npx wrangler secret list
# シークレットの削除
npx wrangler secret delete JWT_SECRETコード内でシークレットにアクセスする場合は、Bindingsに型を追加します:
type Bindings = {
DB: D1Database
JWT_SECRET: string // wrangler secret putで設定した値
}モニタリングとログ — wrangler tail・Cloudflare Analytics
# リアルタイムログストリーム
npx wrangler tail
# フィルタリング(エラーのみ)
npx wrangler tail --status error
# JSON形式で出力
npx wrangler tail --format jsonCloudflare DashboardのWorkersセクションでは、リクエスト数・エラー率・CPU使用時間・エッジレイテンシをリアルタイムで確認できます。
料金体系 — 無料枠と有料プラン
Cloudflare Workersは非常に寛大な無料枠が用意されており、小規模プロジェクトであれば無料で運用が可能です。
| プラン | 料金 | リクエスト | CPU時間 |
|---|---|---|---|
| 無料 | 0円/月 | 10万リクエスト/日 | 10ms/リクエスト |
| Workers Paid | 約750円/月(約5ドル) | 1,000万リクエスト/月 | 30ms/リクエスト |
| Workers Paid(超過) | 0.15ドル/100万リク | 追加課金 | 追加課金 |
| Workers Unbound | 使用量ベース | 無制限 | 最大30秒/リクエスト |
KV・D1・R2にも個別の無料枠と従量課金が設定されています。
本番運用のベストプラクティス
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { HTTPException } from 'hono/http-exception'
const app = new Hono<{ Bindings: Bindings }>()
// セキュリティヘッダーとCORS
app.use('*', cors({ origin: ['https://yourdomain.com'] }))
app.use('*', logger())
// グローバルエラーハンドリング
app.onError((err, c) => {
if (err instanceof HTTPException) {
return c.json({ error: err.message }, err.status)
}
console.error('Unhandled error:', err)
return c.json({ error: 'Internal Server Error' }, 500)
})
// 404ハンドリング
app.notFound((c) => c.json({ error: 'Not Found' }, 404))
// シンプルなレート制限(KVベース)
app.use('/api/*', async (c, next) => {
const ip = c.req.header('CF-Connecting-IP') ?? 'unknown'
const key = `ratelimit:${ip}`
const count = parseInt(await c.env.CACHE.get(key) ?? '0')
if (count > 100) {
throw new HTTPException(429, { message: 'Too Many Requests' })
}
await c.env.CACHE.put(key, String(count + 1), { expirationTtl: 60 })
await next()
})
export default appよくある質問(FAQ)
Q1. Cloudflare WorkersのCPU制限は問題になりませんか? 無料プランは10ms、有料プランは30msのCPU制限があります。重いデータ処理は避け、I/Oバウンドな処理(DB・KV操作)中心の設計にすることで制限を回避できます。Workers Unboundプランでは最大30秒まで延長可能です。 Q2. Node.jsのネイティブモジュール(fs、crypto等)は使えますか? fsは使えませんが、cryptoはWeb Crypto APIとして利用可能です。Node.jsの互換レイヤー(nodejs_compat フラグ)を有効化することで多くのNode.jsモジュールが動作します。 Q3. WebSocketは対応していますか? はい。CloudflareはWebSocket接続をサポートしており、Honoの `upgradeWebSocket` ヘルパーで実装できます。Durable Objectsと組み合わせることでリアルタイムアプリも構築可能です。 Q4. D1はどの程度のデータ量まで対応できますか? 1データベースあたり最大10GBまで対応しています(2026年現在)。大規模データが必要な場合は、Cloudflare Hyperdrive経由で外部PostgreSQL・MySQLに接続することを推奨します。 Q5. 既存のExpressアプリをHono×Cloudflare Workersに移行できますか? APIの設計思想が近いため移行は比較的容易です。ただし、Node.js固有のモジュール(path、fs等)の置き換えが必要です。段階的移行として、まず `@hono/node-server` でHonoに移行し、その後Workersに移行する手順が推奨されます。 Q6. Cloudflare WorkersでPrismaは使えますか? PrismaはWrangler D1ドライバー(@prisma/adapter-d1)に対応しており、D1と組み合わせて使用可能です。ただし起動時間の観点から、より軽量なDrizzle ORMの使用を推奨します。
OflightでHono × Cloudflare Workersの開発を加速させませんか
OflightはHonoおよびCloudflare Workersを活用したエッジAPIの設計・開発・運用支援を提供しています。D1・KV・R2・Workers AIを組み合わせたサーバーレスバックエンドの構築から、既存インフラのエッジへの移行まで幅広く対応します。まずはお気軽にご相談ください。 ソフトウェア開発サービスの詳細はこちら
お気軽にご相談ください
お問い合わせ