Honoを使ってみた


「Hono」というWebフレームワークをご存知でしょうか。名前の通り日本語の「炎」に由来するこのフレームワークは、現在急速に注目を集めている次世代のJavaScript/TypeScriptフレームワークです。今回はそれについて書いていきます。
Honoとは何か?
Honoは、Web標準に基づいて構築された小型でシンプル、かつ超高速なWebフレームワークです。Cloudflare Workersをはじめ、Deno、Bun、Vercel、AWS Lambda、Node.jsなど、あらゆるJavaScriptランタイムで動作します。
特徴としては以下のようなものがあります。
- 超高速
HonoのルーターであるRegExpRouterは非常に高速で、線形ループを使用していません。多くの従来ルーターでは、リクエストごとに複数のルート定義を評価する必要があり、Honoではそれらを1つの正規表現にまとめることで評価回数を削減しています。
- 超軽量
hono/tinyプリセットは12KB未満で、Honoには依存関係がなく、Web標準APIのみを使用しています。この軽量性により、起動が速く、メモリ使用量も少なくなっています。
- マルチランタイム対応
Honoの最大の特徴の1つが、複数のランタイムで同じコードが動作することです。以下の環境で動作します。
- Node.js
- Deno
- Bun
- Cloudflare Workers
- AWS Lambda / Lambda@Edge
- Fastly Compute
- Vercel
- その他のエッジ環境
「一度書けば、どこでも動く」 というのがHonoの哲学です。
- バッテリー同梱
Honoには組み込みミドルウェア、カスタムミドルウェア、サードパーティミドルウェア、ヘルパーが含まれています。認証、CORS、ログ、キャッシュなど、Webアプリケーション開発に必要な機能が最初から揃っています。
Honoが適している用途
- REST APIやGraphQL APIの構築
- エッジコンピューティングを活用したアプリケーション
- サーバーレス関数(AWS Lambda、Cloudflare Workersなど)
- マイクロサービス
- リアルタイム性が求められるアプリケーション
- 軽量で高速なプロキシサーバー
環境構築とセットアップ
それでは実際にHonoを使ってみましょう。
前提条件
- Node.js 18以上がインストールされていること
- 基本的なTypeScriptの知識
- ターミナル/コマンドラインの基本操作
プロジェクトの作成
Honoプロジェクトを作成する最も簡単な方法は、公式のCLIツールを使うことです。
npm create hono@latest my-first-hono-app
実行すると、テンプレートの選択を求められます。
? Which template do you want to use?
aws-lambda
bun
cloudflare-pages
cloudflare-workers
deno
fastly
> nodejs
vercel
今回はnodejsを選択してみましょう。
> npx
> create-hono my-first-hono-app
create-hono version 0.19.4
✔ Using target directory … my-first-hono-app
✔ Which template do you want to use? nodejs
✔ Do you want to install project dependencies? Yes
✔ Which package manager do you want to use? npm
✔ Cloning the template
✔ Installing project dependencies
🎉 Copied project files
Get started with: cd my-first-hono-app
プロジェクトが作成されたら、ディレクトリに移動して依存関係をインストールします。
cd my-first-hono-app
npm install
プロジェクト構造
生成されたプロジェクトの構造は以下のようになっています。
my-first-hono-app/
├── src/
│ └── index.ts
├── package.json
├── tsconfig.json
└── README.md
基本的な使い方
Hello Worldアプリケーション
src/index.tsを開くと、以下のようなコードがあります。
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
serve({
fetch: app.fetch,
port: 3000
}, (info) => {
console.log(`Server is running on http://localhost:${info.port}`)
})
このシンプルなコードで、基本的なWebサーバーができあがっています。
開発サーバーの起動
以下のコマンドで開発サーバーを起動します。
npm run dev
ブラウザでhttp://localhost:3000にアクセスすると、「Hello Hono!」と表示されます。
JSONレスポンスの返却
API開発では、JSON形式でデータを返すことが一般的です。Honoでは非常に簡単にJSONレスポンスを返せます。
app.get('/api/hello', (c) => {
return c.json({
message: 'Hello from Hono API!',
timestamp: new Date().toISOString()
})
})
パスパラメータとクエリパラメータ
URLからパラメータを取得する方法も簡単です。
app.get('/posts/:id', (c) => {
const id = c.req.param('id')
const page = c.req.query('page')
return c.json({
postId: id,
page: page || '1'
})
})
http://localhost:3000/posts/123?page=2にアクセスすると、以下のようなレスポンスが返ってきます。
{
"postId": "123",
"page": "2"
}
レスポンスヘッダーの設定
カスタムヘッダーを追加することも可能です。
app.get('/custom', (c) => {
c.header('X-Custom-Header', 'My Value')
c.header('X-Powered-By', 'Hono')
return c.text('Check the headers!')
})
実践的なCRUDアプリケーション
ここからは、より実践的なCRUDアプリケーションを作成してみましょう。
ユーザー管理APIの実装
import { Hono } from 'hono'
type User = {
id: number
name: string
email: string
}
const app = new Hono()
let users: User[] = []
// ユーザー一覧取得 (Read)
app.get('/users', (c) => {
return c.json({ users })
})
// 特定ユーザー取得 (Read)
app.get('/users/:id', (c) => {
const id = parseInt(c.req.param('id'))
const user = users.find(u => u.id === id)
if (!user) {
return c.json({ error: 'User not found' }, 404)
}
return c.json({ user })
})
// ユーザー作成 (Create)
app.post('/users', async (c) => {
const { name, email } = await c.req.json()
const newUser: User = {
id: Date.now(),
name,
email
}
users.push(newUser)
return c.json({ message: 'User created', user: newUser }, 201)
})
// ユーザー更新 (Update)
app.patch('/users/:id', async (c) => {
const id = parseInt(c.req.param('id'))
const { name, email } = await c.req.json()
const userIndex = users.findIndex(u => u.id === id)
if (userIndex === -1) {
return c.json({ error: 'User not found' }, 404)
}
if (name) users[userIndex].name = name
if (email) users[userIndex].email = email
return c.json({ message: 'User updated', user: users[userIndex] })
})
// ユーザー削除 (Delete)
app.delete('/users/:id', (c) => {
const id = parseInt(c.req.param('id'))
const initialLength = users.length
users = users.filter(u => u.id !== id)
if (users.length === initialLength) {
return c.json({ error: 'User not found' }, 404)
}
return c.json({ message: 'User deleted' })
})
export default app
APIのテスト
curlコマンドでAPIをテストしてみましょう。
# ユーザー作成
$ curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name":"太郎","email":"taro@example.com"}'
{"message":"User created","user":{"id":1770086188714,"name":"太郎","email":"taro@example.com"}}
# ユーザー一覧取得
$ curl http://localhost:3000/users
{"users":[{"id":1770086188714,"name":"太郎","email":"taro@example.com"}]}
# 特定ユーザー取得
$ curl http://localhost:3000/users/1770086188714
{"user":{"id":1770086188714,"name":"太郎","email":"taro@example.com"}}
# ユーザー更新
$ curl -X PATCH http://localhost:3000/users/1770086188714 \
-H "Content-Type: application/json" \
-d '{"name":"太郎2"}'
{"message":"User updated","user":{"id":1770086188714,"name":"太郎2","email":"taro@example.com"}}
# ユーザー削除
$ curl -X DELETE http://localhost:3000/users/1770086188714
{"message":"User deleted"}
ミドルウェアの活用
Honoには便利なミドルウェアが豊富に用意されています。
- CORS対応
import { Hono } from 'hono'
import { cors } from 'hono/cors'
const app = new Hono()
app.use('/api/*', cors())
app.get('/api/data', (c) => {
return c.json({ data: 'This supports CORS' })
})
- Basic認証
import { Hono } from 'hono'
import { basicAuth } from 'hono/basic-auth'
const app = new Hono()
app.use(
'/admin/*',
basicAuth({
username: 'admin',
password: 'secret'
})
)
app.get('/admin', (c) => {
return c.text('You are authorized!')
})
- ロギング
import { Hono } from 'hono'
import { logger } from 'hono/logger'
const app = new Hono()
app.use('*', logger())
app.get('/', (c) => {
return c.text('Hello!')
})
バリデーションの実装
Zodと組み合わせることで、型安全なバリデーションが実装できます。
npm install zod @hono/zod-validator
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const app = new Hono()
const userSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150).optional()
})
app.post('/users', zValidator('json', userSchema), async (c) => {
const user = c.req.valid('json')
// ここでuserは型安全
return c.json({
message: 'User created',
user
})
})
JSXによるサーバーサイドレンダリング
HonoにはJSXのサポートが組み込まれており、サーバーサイドでHTMLをレンダリングできます。
ファイル名を.tsxに変更し、以下のように記述します。
import { Hono } from 'hono'
import type { FC } from 'hono/jsx'
const app = new Hono()
const Layout: FC = (props) => {
return (
<html>
<head>
<title>Hono App</title>
</head>
<body>
{props.children}
</body>
</html>
)
}
const Top: FC<{ messages: string[] }> = (props) => {
return (
<Layout>
<h1>Hello Hono!</h1>
<ul>
{props.messages.map((message) => (
<li>{message}</li>
))}
</ul>
</Layout>
)
}
app.get('/', (c) => {
const messages = ['おはよう', 'こんにちは', 'こんばんは']
return c.html(<Top messages={messages} />)
})
export default app
デプロイ
Honoの大きな利点は、様々なプラットフォームにデプロイできることです。
- AWS Lambdaへのデプロイ
Node.jsアダプタを使用することで、AWS Lambdaでも動作します。
import { Hono } from 'hono'
import { handle } from 'hono/aws-lambda'
const app = new Hono()
app.get('/', (c) => c.text('Hello from Lambda!'))
export const handler = handle(app)
- Vercelへのデプロイ
Vercelアダプタを使用します。
import { Hono } from 'hono'
import { handle } from 'hono/vercel'
const app = new Hono()
app.get('/', (c) => c.text('Hello from Vercel!'))
export default handle(app)
まとめ
Honoは、以下のような特徴を持つ次世代のWebフレームワークです。
- 超高速で軽量: 最小限のオーバーヘッドで高速に動作
- マルチランタイム対応: 様々な環境で同じコードが動作
- TypeScriptファースト: 型安全性が高く、開発者体験が優れている
- 豊富なミドルウェア: 実用的な機能が最初から揃っている
- シンプルなAPI: 学習コストが低く、すぐに使い始められる
ぜひ、実際に手を動かしてHonoの開発を体験してみてください。