※本記事には広告(A8・楽天アフィリエイト)リンクが含まれる場合があります。

個人開発でバックエンドAPIを作るとき、何を使えばいいか迷ったことはありませんか?Express.jsはNode.jsに縛られ、Next.jsはフロントも込みで重い。「もっとシンプルに、もっと速く、どこでも動くものが欲しい」——そんな要求に応えるのが Hono.js です。

Hono(炎)は日本人開発者が作った超軽量Webフレームワーク。Cloudflare Workers・Bun・Deno・Node.js・AWS Lambdaなど複数のランタイムで動作し、TypeScriptファーストで設計されています。2026年現在、個人開発者の間で急速に普及しており、GitHubスターも2万を超えています。

本記事では、Hono + Cloudflare Workers + TypeScript の組み合わせで実践的なAPIを作る方法を、セットアップからデプロイまで丁寧に解説します。

Hono.jsとは? なぜ2026年に選ぶべきか

Honoの特徴を一言で表すなら「Web標準に従った、どこでも動く軽量フレームワーク」です。Node.js独自のAPIではなく、ブラウザでも使われる標準のWeb API(Request/Response/Headers)をベースに設計されているため、ランタイムを選びません。

主な特徴

  • 超高速 — ベンチマークでExpress.jsの約10倍のスループット。Cloudflare Workersのエッジで動くとさらに有利
  • 超軽量 — コアバンドルサイズは約14KB。Cloudflare Workers の1MBスクリプト制限にも余裕で収まる
  • TypeScriptファースト — 型定義が充実しており、IDEの補完が効いて開発体験が良い
  • マルチランタイム — Cloudflare Workers・Bun・Deno・Node.js・Fastly Compute@Edgeで動作
  • 豊富なミドルウェア — CORS・認証・Zod バリデーション・JWT など公式ミドルウェアが揃っている

Express.js・Fastify との比較

項目 Hono Express Fastify
ランタイム マルチ(CF Workers含む) Node.jsのみ Node.jsのみ
TypeScript ネイティブ @types必要 対応
バンドルサイズ 〜14KB 〜220KB 〜400KB
Web標準API ✅ 完全準拠 ❌ 独自実装 △ 一部

個人開発でCloudflare Workersを使うなら、HonoはExpressより圧倒的に相性が良いです。

セットアップ:Cloudflare Workers + Hono プロジェクトを作る

前提として、Node.js 18以上Cloudflareアカウントが必要です。Cloudflareのアカウントは無料で作れます。

ステップ1:Wranglerをインストール

Cloudflare Workers の公式CLIである Wrangler をインストールします。

npm install -g wrangler
wrangler login

wrangler login でブラウザが開き、Cloudflareアカウントでログインします。

ステップ2:Honoプロジェクトを作成

Honoの公式CLIでプロジェクトを雛形から作れます。

npm create hono@latest my-api
# テンプレート選択: cloudflare-workers を選ぶ
cd my-api
npm install

生成されたファイル構成はこうなります:

my-api/
├── src/
│   └── index.ts      # エントリーポイント
├── wrangler.toml     # Workers設定
├── package.json
└── tsconfig.json

ステップ3:最初のHelloWorld

生成された src/index.ts を確認してみましょう。

import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello Hono!')
})

export default app

たったこれだけです。cはコンテキストオブジェクトで、リクエスト・レスポンス・環境変数へのアクセスを統一的に提供します。ローカルで動かすには:

npm run dev
# http://localhost:8787 でアクセス可能

広告・楽天アフィリエイト

開発者向けガジェット

PC周辺機器を楽天市場でチェック。

楽天ポイント還元

楽天市場を見る →

広告・楽天アフィリエイト

メカニカルキーボード

打鍵感で選ぶ開発者向けキーボード。

楽天ポイント還元

キーボードを楽天で見る →

ルーティング:RESTful APIを設計する

HonoのルーティングはExpressによく似ていますが、TypeScriptの型推論が強力です。

基本的なHTTPメソッド

import { Hono } from 'hono'

const app = new Hono()

// GETリクエスト
app.get('/users', (c) => {
  return c.json({ users: ['Alice', 'Bob'] })
})

// パスパラメータ
app.get('/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, name: 'Alice' })
})

// POSTリクエスト(BodyはJSONで受け取る)
app.post('/users', async (c) => {
  const body = await c.req.json()
  return c.json({ created: body }, 201)
})

// PUTとDELETE
app.put('/users/:id', async (c) => { /* ... */ })
app.delete('/users/:id', (c) => { /* ... */ })

export default app

ルートグループ化でコードを整理

APIが大きくなってきたら、Honoインスタンスをルート別に分割してマウントできます。

// routes/users.ts
import { Hono } from 'hono'
const users = new Hono()

users.get('/', (c) => c.json({ users: [] }))
users.get('/:id', (c) => c.json({ id: c.req.param('id') }))

export default users

// src/index.ts
import { Hono } from 'hono'
import users from './routes/users'

const app = new Hono()
app.route('/users', users)  // /users/* にマウント

export default app

ミドルウェア:CORS・認証・ロギングを追加

Honoには公式ミドルウェアが充実しています。hono/middlewareから必要なものをインポートするだけです。

CORSミドルウェア

フロントエンドからAPIを叩くときに必須のCORSを一行で追加できます。

import { cors } from 'hono/cors'

app.use('/api/*', cors({
  origin: ['https://yourapp.pages.dev'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
}))

// 全ルートに適用するなら
app.use('*', cors())

Bearer Token認証

import { bearerAuth } from 'hono/bearer-auth'

app.use('/admin/*', bearerAuth({
  token: 'my-secret-token'  // 実際はenv変数から取得
}))

カスタムミドルウェア(ロギング)

独自のミドルウェアも簡単に書けます。next()で次の処理に進む点はExpressと同じです。

app.use('*', async (c, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  console.log(`${c.req.method} ${c.req.url} - ${c.res.status} (${ms}ms)`)
})

バリデーション:Zodで型安全なリクエスト処理

リクエストボディのバリデーションにはZodとの組み合わせが定番です。@hono/zod-validatorを使うと、バリデーション失敗時に自動で400エラーを返してくれます。

npm install zod @hono/zod-validator
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const createUserSchema = z.object({
  name: z.string().min(1).max(50),
  email: z.string().email(),
  age: z.number().int().min(18).optional(),
})

app.post(
  '/users',
  zValidator('json', createUserSchema),
  async (c) => {
    // バリデーション済みの型安全
        なデータ
    const data = c.req.valid('json')
    // data.name, data.email は型推論が効いている
    return c.json({ created: data }, 201)
  }
)

これでバリデーション漏れによるバグを型レベルで防げます。個人開発でも手抜きせずに入れておくべき仕組みです。

広告・楽天アフィリエイト

PCモニター

デュアルディスプレイや高解像度モデルをチェック。

楽天ポイント還元

モニターを楽天で見る →

広告・楽天アフィリエイト

プログラミング技術書

実践寄りの技術書・参考書を探す。

楽天ポイント還元

技術書を楽天で見る →

Cloudflare D1・KVとの連携

Cloudflare WorkersにはD1(SQLiteベースのDB)やKV(キー・バリューストア)が統合されています。Honoと組み合わせると、データベース付きAPIをサーバーレスで無料運用できます。

D1(SQLiteデータベース)を使う

まずwrangler.tomlにD1の設定を追加します。

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Honoのコンテキストから環境変数経由でD1にアクセスします。

type Bindings = {
  DB: D1Database
}

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

app.get('/users', async (c) => {
  const db = c.env.DB
  const { results } = await db
    .prepare('SELECT * FROM users LIMIT 10')
    .all()
  return c.json(results)
})

app.post('/users', async (c) => {
  const { name, email } = await c.req.json()
  const db = c.env.DB
  await db
    .prepare('INSERT INTO users (name, email) VALUES (?, ?)')
    .bind(name, email)
    .run()
  return c.json({ message: 'Created' }, 201)
})

KV(キー・バリューストア)でキャッシュ

type Bindings = {
  CACHE: KVNamespace
}

app.get('/config', async (c) => {
  const cached = await c.env.CACHE.get('config')
  if (cached) return c.json(JSON.parse(cached))

  // DB等から取得してキャッシュ
  const config = { theme: 'dark', lang: 'ja' }
  await c.env.CACHE.put('config', JSON.stringify(config), {
    expirationTtl: 3600  // 1時間
  })
  return c.json(config)
})

D1の無料枠は1日50,000回の読み込み・10万行のストレージまで無料です。個人開発の規模なら十分すぎるほどです。

エラーハンドリングとHTTP例外

Honoには組み込みのHTTP例外クラスがあります。Expressのようにres.status(404).json(...)と書く必要がなく、コードが簡潔になります。

import { HTTPException } from 'hono/http-exception'

app.get('/users/:id', async (c) => {
  const id = c.req.param('id')
  const user = await findUser(id)

  if (!user) {
    throw new HTTPException(404, { message: 'User not found' })
  }

  return c.json(user)
})

// グローバルエラーハンドラ
app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return c.json({ error: err.message }, err.status)
  }
  console.error(err)
  return c.json({ error: 'Internal server error' }, 500)
})

グローバルエラーハンドラを設定しておくことで、内部の詳細エラーをユーザーに見せずに済みます。セキュリティ面でも重要な実装です。

デプロイ:本番環境へ公開する

デプロイはコマンド一発です。

npm run deploy
# または
wrangler deploy<
        /code>

デプロイが成功すると、https://my-api.your-account.workers.dev のようなURLが発行されます。カスタムドメインも無料で設定できます。

シークレットの管理

APIキーなどの機密情報はwrangler.tomlに書かず、wrangler secretコマンドで管理します。

# シークレットをセット(入力は非表示)
wrangler secret put API_KEY

# シークレット一覧を確認
wrangler secret list

コード内ではc.env.API_KEYでアクセスできます。Gitに機密情報を含めない運用ができます。

実践例:WebページのステータスチェックAPIを作る

最後に、実用的な例として「URLの死活監視APIエンドポイント」を作ってみましょう。ユーザーが登録したURLを定期チェックするAPI——これはまさに、当サイトで紹介している PagePulse(Webページ変更監視ツール)のようなサービスのバックエンドに使えるアーキテクチャです。

import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
import { cors } from 'hono/cors'

type Bindings = {
  DB: D1Database
}

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

app.use('*', cors({
  origin: ['https://your-frontend.pages.dev'],
}))

// URL登録
const registerSchema = z.object({
  url: z.string().url(),
  label: z.string().max(100).optional(),
})

app.post('/monitors', zValidator('json', registerSchema), async (c) => {
  const { url, label } = c.req.valid('json')
  const db = c.env.DB

  await db
    .prepare('INSERT INTO monitors (url, label, status) VALUES (?, ?, ?)')
    .bind(url, label ?? url, 'unknown')
    .run()

  return c.json({ message: 'Monitor registered', url }, 201)
})

// ステータス確認
app.get('/monitors/:id/check', async (c) => {
  const id = c.req.param('id')
  const db = c.env.DB
  const monitor = await db
    .prepare('SELECT * FROM monitors WHERE id = ?')
    .bind(id)
    .first()

  if (!monitor) throw new HTTPException(404, { message: 'Monitor not found' })

  const startTime = Date.now()
  let status = 'down'
  let statusCode: number | null = null

  try {
    const res = await fetch(monitor.url as string, {
      method: 'HEAD',
      signal: AbortSignal.timeout(5000)
    })
    statusCode = res.status
    status = res.ok ? 'up' : 'down'
  } catch {
    status = 'down'
  }

  const responseTime = Date.now() - startTime

  await db
    .prepare('UPDATE monitors SET status = ?, last_checked = datetime("now") WHERE id = ?')
    .bind(status, id)
    .run()

  return c.json({ id, status, statusCode, responseTimeMs: responseTime })
})

export default app

このコードはCloudflare Workersのエッジで動くため、世界中のどこからでも低レイテンシで監視できます。Honoの型安全なルーティングのおかげで、IDEの補完もバッチリ効きます。

広告・楽天アフィリエイト

ガジェット・オーディオ

イヤホン・便利ガジェットをポイント還元で。

楽天ポイント還元

ガジェットを楽天で見る →

まとめ:Honoが個人開発に最適な理由

Hono + Cloudflare Workers の組み合わせは、2026年現在の個人開発バックエンドとして最有力の選択肢の一つです。

  • 無料でAPIサーバーを運用できる(Workers無料枠:1日10万リクエスト)
  • TypeScriptで型安全に開発できる
  • デプロイが一瞬wrangler deployで完了)
  • D1・KV・R2などCloudflareのエコシステムと統合しやすい
  • コードが簡潔(ボイラープレートが最小限)

複雑なインフラ管理なしに、コードを書いてデプロイするだけでAPIが動く。個人開発者にとって、これ以上ありがたい環境はないでしょう。

バックエンドが完成したら、フロントエンドのデプロイ先として Cloudflare Pages を使うのがおすすめです。APIとフロントを同じCloudflareエコシステムに揃えることで、レイテンシを最小化できます。

また、作ったサービスの稼働状況を外部に伝えたい場合は、ステータスページも用意しておきましょう。StatusCraftは個人開発者でも無料で使えるステータスページツールで、APIのダウン情報をリアルタイムに公開できます。

🔥 今日からHonoを試してみよう

Cloudflareアカウントは無料。npm create hono@latest を実行するだけで、5分後にはエッジAPIが動きます。

Hono公式ドキュメントを見る →

ウェブサービスを作ったら、外部からのアクセスが正常に続いているか確認する仕組みも大切です。PagePulseのような監視ツールを使えば、APIが落ちたときにすぐ気づけます。個人開発の信頼性向上に役立てましょう。

さらに、ChromeでAPIレスポンスを素早く確認したいときは、QuickSummary(AI要約Chrome拡張)も合わせてインストールしておくと、長いJSONレスポンスやドキュメントをAIで要約して確認できて便利です。