Cloudflare Workers + HonoJSチュートリアル①：軽量・高速なWebフレームワークの始め方


## 1. はじめに

Hono は Node.js / Deno / Bun など複数のランタイムで動作する軽量・高速なフレームワークです。  
もともと Cloudflare Workers との相性を考慮して設計されていますが、Node.js で使う場合は専用のアダプターを導入する必要があります。  
本チュートリアルでは、Node.js 環境で Hono をセットアップし、CRUD (Create, Read, Update, Delete) を実装したサンプルを紹介します。

---

## 2. 環境の準備

1. **Node.js のインストール**  
   公式サイトなどから Node.js (LTS 推奨) をインストールします。

2. **Wrangler のインストールとログイン**  
   以下のコマンドを実行し、Wrangler をグローバルインストールしてログインします。
   ```bash
   npm install -g wrangler
   wrangler --version
   wrangler login
   ```

3. **新規プロジェクトの作成**  
   Wrangler を使って新規プロジェクトを初期化します。
   ```bash
   wrangler init -y my-hono-crud
   cd my-hono-crud
   ```

4. **必要パッケージのインストール**  
   ```bash
   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` はメインのエントリーポイントとしてルーティング設定のみ行うようにします。

```bash
src/
  index.ts
  users/
    index.ts
```

---

## 4. コード例

### 4-1. src/users/index.ts

以下の例では、メモリ上の配列 `users` を使った CRUD 実装をまとめています（実際はデータベースを利用する想定で書き換えてください）。

```typescript
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` 関数を使ってサーバを起動します。

```typescript
import { Hono } from 'hono'
import userApp from './users/index'

const app = new Hono()

// "/users" 以下のルーティングを userApp に委譲
app.route('/users', userApp)

export default app
```

---

## 5. アプリの起動

上記ファイルを保存したら、以下のコマンドでサーバを起動します。

```bash
npm start
```

---

## 6. curl コマンドによる動作確認

`http://localhost:3000` をエンドポイントとして、各ルートを curl でテストしてみましょう。

### 6-1. Create (POST /users)

```bash
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice"}' \
  http://localhost:8787/users
```

レスポンス例:
```json
{
  "message": "User created",
  "user": {
    "id": 1691131623455,
    "name": "Alice"
  }
}
```

### 6-2. Read (GET /users, GET /users/:id)

- **全ユーザーの取得**  
  ```bash
  curl http://localhost:8787/users
  ```

- **特定ユーザーの取得**  
  ```bash
  curl http://localhost:8787/users/1691131623455
  ```

### 6-3. Update (PATCH /users/:id)

```bash
curl -X PATCH \
  -H "Content-Type: application/json" \
  -d '{"name":"Updated Alice"}' \
  http://localhost:8787/users/1691131623455
```

### 6-4. Delete (DELETE /users/:id)

```bash
curl -X DELETE http://localhost:8787/users/1691131623455
```

---


## 7. テストの実装例

ここではシンプルなテストフレームワークとして [Jest](https://jestjs.io/) を使用した例を紹介します。  
実装対象のコードを `src/users/index.ts` に分割している場合、テストファイルは `__tests__/users.test.ts` のように配置します。  
実際にテストを実行するためには、`jest` や TypeScript 向けの設定が必要です。

### 7-1. テストコード例

`test/users.test.ts` (例)

```typescript
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` などを活用します。

テストを実行するには、以下のコマンドを実行します。

```bash
npm test
```

---

## 8. Cloudflare Workers へのデプロイ

### 8-1. wrangler.toml の設定

Cloudflare Workers にデプロイするためには、`wrangler.toml` を用意します。`main` にはビルド後のエントリーポイント (例: `dist/index.js`) を指定します。

```toml
name = "my-hono-crud"
main = "dist/index.js"
compatibility_date = "2023-10-01"
```

- `name`: プロジェクト名
- `main`: ビルド後のワーカーエントリーポイント
- `compatibility_date`: Cloudflare Workers の互換性日

### 8-2. デプロイ方法

```bash
npm run deploy
```

数秒ほど待つと、Cloudflare 側にデプロイが完了し、URL が割り当てられます。  
これで Cloudflare Workers 上で動作する Hono アプリが公開されます。

## 9. まとめ

- **Hono** は複数のランタイムで動作し、軽量かつ高速なフレームワークです。  
- **Node.js** で利用する場合は `@hono/node-server` を用いて `serve()` を呼び出す形が簡単です。  
- **CRUD** の実装はデータベースを使う場合が多いですが、最初の学習やプロトタイプではメモリ上のデータでも十分試せます。  
- コードを整理するために、関連するエンドポイントをディレクトリ単位で管理する方法もおすすめです。  
- **curl** コマンドなどを使うと、HTTP リクエストを手軽にテストできます。

これでローカル環境で動作する簡単な Hono CRUD アプリのチュートリアルは終了です。ぜひ実際の開発に取り入れてみてください。

---
### 会社紹介

**株式会社 Mosaica**  
最先端テクノロジーで社会課題を解決し、持続可能な未来を創造する IT カンパニー。
AI ソリューション、クラウド統合、DX 推進、経営コンサルティングなど包括的なサービスでビジネス変革を支援しています。  

詳しくは [公式サイト](https://mosaica.co.jp/) までお気軽にご相談ください。
公式サイト： https://mosaica.co.jp/