1. はじめに
Hono は Node.js / Deno / Bun など複数のランタイムで動作する軽量・高速なフレームワークです。
もともと Cloudflare Workers との相性を考慮して設計されていますが、Node.js で使う場合は専用のアダプターを導入する必要があります。
本チュートリアルでは、Node.js 環境で Hono をセットアップし、CRUD (Create, Read, Update, Delete) を実装したサンプルを紹介します。
2. 環境の準備
-
Node.js のインストール
公式サイトなどから Node.js (LTS 推奨) をインストールします。 -
Wrangler のインストールとログイン
以下のコマンドを実行し、Wrangler をグローバルインストールしてログインします。npm install -g wrangler wrangler --version wrangler login
-
新規プロジェクトの作成
Wrangler を使って新規プロジェクトを初期化します。wrangler init -y my-hono-crud cd my-hono-crud
-
必要パッケージのインストール
npm install hono npm install --save-dev typescript ts-node @types/node
-
hono
: 本体のフレームワーク -
typescript
,ts-node
,@types/node
: TypeScript 開発用パッケージ
-
3. ディレクトリ構成
src
ディレクトリ配下に users
ディレクトリを作成し、そこにユーザー CRUD ロジックをまとめます。src/index.ts
はメインのエントリーポイントとしてルーティング設定のみ行うようにします。
src/
index.ts
users/
index.ts
4. コード例
4-1. src/users/index.ts
以下の例では、メモリ上の配列 users
を使った CRUD 実装をまとめています(実際はデータベースを利用する想定で書き換えてください)。
import { Hono } from 'hono'
type User = { id: number; name: string }
let users: User[] = []
const userApp = new Hono()
// Create: ユーザー作成 (POST /users)
userApp.post('/', async (c) => {
const { name } = await c.req.json()
const newUser: User = {
id: Date.now(),
name
}
users.push(newUser)
return c.json({ message: 'User created', user: newUser })
})
// Read: 全ユーザー取得 (GET /users)
userApp.get('/', (c) => {
return c.json(users)
})
// Read: 特定ユーザー取得 (GET /users/:id)
userApp.get('/:id', (c) => {
const userId = parseInt(c.req.param('id'))
const user = users.find((u) => u.id === userId)
if (!user) {
return c.json({ error: 'User not found' }, 404)
}
return c.json(user)
})
// Update: ユーザー更新 (PATCH /users/:id)
userApp.patch('/:id', async (c) => {
const userId = parseInt(c.req.param('id'))
const { name } = await c.req.json()
const userIndex = users.findIndex((u) => u.id === userId)
if (userIndex === -1) {
return c.json({ error: 'User not found' }, 404)
}
users[userIndex].name = name
return c.json({ message: 'User updated', user: users[userIndex] })
})
// Delete: ユーザー削除 (DELETE /users/:id)
userApp.delete('/:id', (c) => {
const userId = parseInt(c.req.param('id'))
const userIndex = users.findIndex((u) => u.id === userId)
if (userIndex === -1) {
return c.json({ error: 'User not found' }, 404)
}
const deletedUser = users.splice(userIndex, 1)[0]
return c.json({ message: 'User deleted', user: deletedUser })
})
export default userApp
4-2. src/index.ts
メインの src/index.ts
では、Hono のインスタンスを作成し、/users
へのルートを users/index.ts
から読み込みます。最後に @hono/node-server
の serve
関数を使ってサーバを起動します。
import { Hono } from 'hono'
import userApp from './users/index'
const app = new Hono()
// "/users" 以下のルーティングを userApp に委譲
app.route('/users', userApp)
export default app
5. アプリの起動
上記ファイルを保存したら、以下のコマンドでサーバを起動します。
npm start
6. curl コマンドによる動作確認
http://localhost:3000
をエンドポイントとして、各ルートを curl でテストしてみましょう。
6-1. Create (POST /users)
curl -X POST \
-H "Content-Type: application/json" \
-d '{"name":"Alice"}' \
http://localhost:8787/users
レスポンス例:
{
"message": "User created",
"user": {
"id": 1691131623455,
"name": "Alice"
}
}
6-2. Read (GET /users, GET /users/:id)
-
全ユーザーの取得
curl http://localhost:8787/users
-
特定ユーザーの取得
curl http://localhost:8787/users/1691131623455
6-3. Update (PATCH /users/:id)
curl -X PATCH \
-H "Content-Type: application/json" \
-d '{"name":"Updated Alice"}' \
http://localhost:8787/users/1691131623455
6-4. Delete (DELETE /users/:id)
curl -X DELETE http://localhost:8787/users/1691131623455
7. テストの実装例
ここではシンプルなテストフレームワークとして Jest を使用した例を紹介します。
実装対象のコードを src/users/index.ts
に分割している場合、テストファイルは __tests__/users.test.ts
のように配置します。
実際にテストを実行するためには、jest
や TypeScript 向けの設定が必要です。
7-1. テストコード例
test/users.test.ts
(例)
import { Hono } from 'hono'
import { unstable_dev } from 'wrangler' // Cloudflare Workers でのテストを想定
import userApp from '../src/users'
describe('Users CRUD', () => {
let app: Hono
let server: any
beforeAll(async () => {
// テスト用の Hono インスタンス
app = new Hono()
app.route('/users', userApp)
// Cloudflare Workers でのテストを想定して unstable_dev を利用
// Node.js でのローカルテストであれば、supertest + Node.js アダプターなどを使用してもOK
server = await unstable_dev(
'./src/index.ts',
{ experimental: { disableExperimentalWarning: true } },
{}
)
})
afterAll(async () => {
await server.stop()
})
test('Create user', async () => {
const payload = JSON.stringify({ name: 'Alice' })
const res = await server.fetch('/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: payload
})
const data = await res.json()
expect(res.status).toBe(200)
expect(data.message).toBe('User created')
expect(data.user.name).toBe('Alice')
})
test('Get all users', async () => {
const res = await server.fetch('/users')
const data = await res.json()
expect(res.status).toBe(200)
expect(Array.isArray(data)).toBe(true)
expect(data.length).toBeGreaterThan(0)
})
})
-
Cloudflare Workers 環境でテストする場合は、
wrangler
が提供するunstable_dev
などを活用します。
テストを実行するには、以下のコマンドを実行します。
npm test
8. Cloudflare Workers へのデプロイ
8-1. wrangler.toml の設定
Cloudflare Workers にデプロイするためには、wrangler.toml
を用意します。main
にはビルド後のエントリーポイント (例: dist/index.js
) を指定します。
name = "my-hono-crud"
main = "dist/index.js"
compatibility_date = "2023-10-01"
-
name
: プロジェクト名 -
main
: ビルド後のワーカーエントリーポイント -
compatibility_date
: Cloudflare Workers の互換性日
8-2. デプロイ方法
npm run deploy
数秒ほど待つと、Cloudflare 側にデプロイが完了し、URL が割り当てられます。
これで Cloudflare Workers 上で動作する Hono アプリが公開されます。
9. まとめ
- Hono は複数のランタイムで動作し、軽量かつ高速なフレームワークです。
-
Node.js で利用する場合は
@hono/node-server
を用いてserve()
を呼び出す形が簡単です。 - CRUD の実装はデータベースを使う場合が多いですが、最初の学習やプロトタイプではメモリ上のデータでも十分試せます。
- コードを整理するために、関連するエンドポイントをディレクトリ単位で管理する方法もおすすめです。
- curl コマンドなどを使うと、HTTP リクエストを手軽にテストできます。
これでローカル環境で動作する簡単な Hono CRUD アプリのチュートリアルは終了です。ぜひ実際の開発に取り入れてみてください。