Hono の使い方 — Web Standards ベースの超高速Webフレームワーク
一言でいうと
Hono(炎🔥)は、Web Standards API 上に構築された超軽量・超高速なWebフレームワークです。Cloudflare Workers、Deno、Bun、AWS Lambda、Node.js など、あらゆるJavaScriptランタイムで同一コードが動作します。
どんな時に使う?
- エッジコンピューティング環境でのAPI構築 — Cloudflare Workers や Fastly Compute など、エッジランタイム上でREST APIやBFFを構築したい場合。Express では動かない環境でも Hono なら動きます
- マルチランタイム対応のWebアプリケーション — 開発はNode.js、本番はBunやDeno、デプロイ先はAWS Lambdaなど、ランタイムを柔軟に切り替えたいプロジェクト
- 軽量で高速なAPIサーバー — バンドルサイズが12kB未満(
hono/tiny)で依存ゼロ。コールドスタートが重要なサーバーレス環境や、パフォーマンスを最優先するAPIサーバーに最適
インストール
# npm
npm install hono
# yarn
yarn add hono
# pnpm
pnpm add hono
プロジェクトのスキャフォールディングには以下が便利です:
npm create hono@latest
基本的な使い方
最もシンプルなHonoアプリケーションの例です。
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello, Hono!')
})
app.get('/json', (c) => {
return c.json({ message: 'Hello, Hono!', timestamp: Date.now() })
})
app.post('/posts', async (c) => {
const body = await c.req.json<{ title: string; content: string }>()
return c.json({ id: 1, ...body }, 201)
})
export default app
Node.js で起動する場合は @hono/node-server を使います:
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello from Node.js!'))
serve({
fetch: app.fetch,
port: 3000,
})
console.log('Server is running on http://localhost:3000')
よく使うAPI
1. ルーティング(HTTPメソッド & パスパラメータ)
import { Hono } from 'hono'
const app = new Hono()
// 基本的なHTTPメソッド
app.get('/users', (c) => c.json([]))
app.post('/users', (c) => c.json({ created: true }, 201))
app.put('/users/:id', (c) => c.json({ updated: true }))
app.delete('/users/:id', (c) => c.json({ deleted: true }))
// パスパラメータの取得
app.get('/users/:id', (c) => {
const id = c.req.param('id')
return c.json({ id })
})
// ワイルドカード
app.get('/files/*', (c) => {
const path = c.req.path
return c.text(`File path: ${path}`)
})
// クエリパラメータ
app.get('/search', (c) => {
const query = c.req.query('q')
const page = c.req.query('page') ?? '1'
return c.json({ query, page: Number(page) })
})
2. ミドルウェア
Hono には豊富なビルトインミドルウェアが用意されています。
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { prettyJSON } from 'hono/pretty-json'
import { bearerAuth } from 'hono/bearer-auth'
import { secureHeaders } from 'hono/secure-headers'
const app = new Hono()
// グローバルミドルウェア
app.use('*', logger())
app.use('*', secureHeaders())
app.use('*', prettyJSON())
// 特定パスにCORS
app.use('/api/*', cors({
origin: 'https://example.com',
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
}))
// 認証が必要なルート
app.use('/admin/*', bearerAuth({ token: 'my-secret-token' }))
app.get('/admin/dashboard', (c) => {
return c.json({ message: 'Welcome, admin!' })
})
// カスタムミドルウェア
app.use('*', async (c, next) => {
const start = Date.now()
await next()
const duration = Date.now() - start
c.header('X-Response-Time', `${duration}ms`)
})
3. ルートグループ化(app.route / app.basePath)
import { Hono } from 'hono'
// サブアプリケーションとしてルートを分割
const api = new Hono()
api.get('/users', (c) => c.json({ users: [] }))
api.get('/users/:id', (c) => {
const id = c.req.param('id')
return c.json({ id, name: 'Taro' })
})
const posts = new Hono()
posts.get('/', (c) => c.json({ posts: [] }))
posts.post('/', async (c) => {
const body = await c.req.json()
return c.json(body, 201)
})
// メインアプリにマウント
const app = new Hono().basePath('/api/v1')
app.route('/users', api)
app.route('/posts', posts)
// /api/v1/users, /api/v1/posts でアクセス可能
export default app
4. バリデーション(hono/validator & Zod連携)
import { Hono } from 'hono'
import { validator } from 'hono/validator'
const app = new Hono()
// 組み込みバリデータ
app.post(
'/posts',
validator('json', (value, c) => {
const { title, content } = value as { title?: string; content?: string }
if (!title || typeof title !== 'string') {
return c.json({ error: 'title is required' }, 400)
}
if (!content || typeof content !== 'string') {
return c.json({ error: 'content is required' }, 400)
}
return { title, content }
}),
(c) => {
const { title, content } = c.req.valid('json')
return c.json({ id: 1, title, content }, 201)
}
)
Zod と @hono/zod-validator を組み合わせると、より型安全なバリデーションが可能です:
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const postSchema = z.object({
title: z.string().min(1).max(100),
content: z.string().min(1),
tags: z.array(z.string()).optional(),
})
const app = new Hono()
app.post('/posts', zValidator('json', postSchema), (c) => {
const data = c.req.valid('json') // 型が自動推論される
return c.json({ id: 1, ...data }, 201)
})
※
@hono/zod-validatorは別途npm install @hono/zod-validator zodが必要です。
5. 型安全なRPCクライアント(hc)
Hono の大きな特徴の一つが、サーバー側のルート定義からクライアント側の型を自動生成できる RPC 機能です。
// server.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const app = new Hono()
.get('/api/users', (c) => {
return c.json({ users: [{ id: 1, name: 'Taro' }] })
})
.post(
'/api/users',
zValidator('json', z.object({ name: z.string() })),
(c) => {
const { name } = c.req.valid('json')
return c.json({ id: 2, name }, 201)
}
)
export type AppType = typeof app
export default app
// client.ts
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('http://localhost:3000')
// 完全に型安全なAPIコール
const res = await client.api.users.$get()
const data = await res.json() // { users: { id: number; name: string }[] }
const createRes = await client.api.users.$post({
json: { name: 'Hanako' }, // 型チェックされる
})
類似パッケージとの比較
| 特徴 | Hono | Express | Fastify | Elysia |
|---|---|---|---|---|
| バンドルサイズ | ~12kB (tiny) | ~200kB | ~400kB | ~50kB |
| TypeScript | ファーストクラス | 型定義別途 | 良好 | ファーストクラス |
| Web Standards準拠 | ✅ | ❌ | ❌ | 部分的 |
| マルチランタイム | ✅ (全主要ランタイム) | Node.js中心 | Node.js中心 | Bun専用 |
| エッジ対応 | ✅ | ❌ | ❌ | ❌ |
| ミドルウェア | 豊富なビルトイン | 巨大なエコシステム | プラグイン方式 | 独自エコシステム |
| RPC クライアント | ✅ 組み込み | ❌ | ❌ | ✅ (Eden Treaty) |
| 依存関係 | ゼロ | ~30 | ~15 | ~5 |
選定の目安:
- Express からの移行・エッジ対応が必要 → Hono
- Node.js 専用で最大スループット → Fastify
- Bun 専用で最高のDX → Elysia
- 既存のExpressエコシステムを活用 → Express
注意点・Tips
ランタイムごとのエントリポイント
Hono はマルチランタイム対応ですが、起動方法はランタイムによって異なります。
// Cloudflare Workers / Bun / Deno — export default でOK
export default app
// Node.js — @hono/node-server が必要
import { serve } from '@hono/node-server'
serve({ fetch: app.fetch, port: 3000 })
// AWS Lambda — @hono/aws-lambda が必要
import { handle } from 'hono/aws-lambda'
export const handler = handle(app)
c.req.json() は一度しか読めない
Request body は Web Standards の仕様上、一度しか消費できません。ミドルウェアで body を読んだ場合、ハンドラで再度読むことはできません。バリデーションミドルウェアを使えば c.req.valid() で安全に取得できます。
ルート定義のチェーンを忘れない(RPC使用時)
RPC クライアントの型推論を正しく機能させるには、ルート定義をメソッドチェーンで書く必要があります。
// ✅ 正しい — チェーンで型が伝播する
const app = new Hono()
.get('/api/users', (c) => c.json([]))
.post('/api/users', (c) => c.json({}, 201))
// ❌ 型が失われる
const app = new Hono()
app.get('/api/users', (c) => c.json([]))
app.post('/api/users', (c) => c.json({}, 201))
hono/tiny プリセットでさらに軽量化
バンドルサイズを極限まで削りたい場合は hono/tiny を使えます。ルーターが PatternRouter に切り替わり、RegExpRouter ほどの速度は出ませんが、サイズは大幅に削減されます。
import { Hono } from 'hono/tiny'
環境変数・Bindings の型定義
Cloudflare Workers の Bindings や環境変数に型を付けるには、ジェネリクスを活用します。
type Bindings = {
DATABASE_URL: string
MY_KV: KVNamespace
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/', (c) => {
const dbUrl = c.env.DATABASE_URL // 型安全
return c.text(dbUrl)
})
まとめ
Hono は「Web Standards に準拠し、どこでも動く」という明確な設計思想を持つ、モダンなWebフレームワークです。ゼロ依存・超軽量でありながら、ミドルウェア、バリデーション、型安全なRPCクライアントまで備えており、エッジコンピューティング時代のAPIサーバー構築において第一選択肢となるフレームワークと言えます。Express に慣れた開発者であれば、ほぼ学習コストなく移行でき、TypeScript との親和性の高さに驚くはずです。