【React × Cloudflare Workers × Hono入門】第1弾:HonoでToDoアプリの爆速CRUD APIを作ろう!

webapp-todo-1 プログラミング

はじめに

今回から新シリーズとして、今最も注目されているモダンなWeb開発スタック「React × Cloudflare Workers × Hono」を使ったサーバーレスWebアプリ(ToDoアプリ)の開発に挑戦します!

「サーバーレス」や「API開発」と聞くと難しく感じられるかもしれませんが、Webフレームワークの Hono を使うと、驚くほどシンプルかつ直感的にバックエンド(API)を構築できます。

第1弾の今回は、まず環境構築からスタートし、Honoを使ってToDoの追加・表示・更新・削除(CRUD)ができるWeb APIを爆速で作っていきます!

1. 今回作る構成とロードマップ

本シリーズでは最終的に、以下のフルスタック構成で世界中に無料公開(デプロイ)できるアプリを目指します。

[ フロントエンド (React) ]
         │ 
    fetch通信 (JSON)
         │
         ▼
[ バックエンド (Hono / Cloudflare Workers) ] 👈 ★今回はここを作成!
         │
         ▼
[ データベース (Cloudflare D1) ] 👈 ※第2弾で連携!

全体のロードマップ

  1. 第1弾(今回):HonoでバックエンドAPI構築(インメモリで速習)
  2. 第2弾: Cloudflare D1(DB)を連携してデータを永続化する
  3. 第3弾: React (Vite) でフロントエンド画面を作ってAPIと接続する
  4. 第4弾: Cloudflare Pages / Workers へ世界一括デプロイ!

2. 環境構築(Honoプロジェクトの作成)

さっそく、Cloudflare Workers + Hono のプロジェクトを立ち上げましょう。 ターミナル(またはコマンドプロンプト)を開き、以下のコマンドを実行します。

npm create hono@latest my-todo-api

セットアップの質問には以下のように答えて進めます:

  • Which template do you want to use?: cloudflare-workers を選択
  • Do you want to use TypeScript?: no(※JavaScriptでシンプルに進める場合。TypeScriptでもOK)
  • Do you want to install project dependencies?: Yes

プロジェクトが作成されたら、作成されたディレクトリに移動します。

cd my-todo-api

3. CRUD APIを実装しよう!

それでは src/index.js(または src/index.ts)を開き、コードを以下のように書き換えます。

今回は学習のハードルを下げるため、データはメモリ上の配列(todos)で管理します。

import { Hono } from 'hono'
import { cors } from 'hono/cors'

const app = new Hono()

// 1. フロントエンド(React)からの接続を許可するCORS設定
app.use('/api/*', cors())

// 2. テスト用の初期データ(インメモリ配列)
let todos = [
  { id: '1', title: '部屋の掃除をする', completed: false },
  { id: '2', title: 'Reactの勉強をする', completed: true },
]

// --------------------------------------------------
// READ: ToDo一覧の取得
// --------------------------------------------------
app.get('/api/todos', (c) => {
  return c.json(todos)
})

// --------------------------------------------------
// CREATE: 新しいToDoの追加
// --------------------------------------------------
app.post('/api/todos', async (c) => {
  const body = await c.req.json()

  // タイトルが空の場合はエラー(400)を返す
  if (!body.title || !body.title.trim()) {
    return c.json({ error: 'タイトルは必須です' }, 400)
  }

  const newTodo = {
    id: Date.now().toString(), // 簡易的なユニークID
    title: body.title,
    completed: false,
  }

  todos.push(newTodo)
  return c.json(newTodo, 201) // 201 Created
})

// --------------------------------------------------
// UPDATE: ToDoの更新(完了フラグの切替など)
// --------------------------------------------------
app.put('/api/todos/:id', async (c) => {
  const id = c.req.param('id')
  const body = await c.req.json()

  // 該当するToDoを探して更新する
  let updatedTodo = null
  todos = todos.map((todo) => {
    if (todo.id === id) {
      updatedTodo = { ...todo, ...body }
      return updatedTodo
    }
    return todo
  })

  if (!updatedTodo) {
    return c.json({ error: '該当するToDoが見つかりません' }, 404)
  }

  return c.json(updatedTodo)
})

// --------------------------------------------------
// DELETE: ToDoの削除
// --------------------------------------------------
app.delete('/api/todos/:id', (c) => {
  const id = c.req.param('id')
  
  // 指定されたID以外のToDoを残す(フィルタリング)
  todos = todos.filter((todo) => todo.id !== id)

  return c.json({ message: '削除が完了しました' })
})

export default app

4. ここがスゴイ!Honoのポイント解説

わずか60行程度のコードですが、Web APIに必要な処理がすべて詰まっています!

① c.json() で一発レスポンス

Expressなど従来のフレームワークだとヘッダー指定が必要だったJSONレスポンスも、Honoなら return c.json(データ) と書くだけで自動的に Content-Type: application/json を付与して返してくれます。

② 直感的なパラメータ取得

  • URL内のID(/api/todos/:id):c.req.param(‘id’) で取得
  • POSTやPUTのリクエストボディ(JSON):await c.req.json() で取得

③ CORSミドルウェアが標準用意されている

将来的にReact(ポート3000など)からAPI(ポート8787など)を呼び出す際、ブラウザのセキュリティ制限(CORSエラー)にかかります。Honoなら app.use(‘/api/*’, cors()) を1行書くだけで解決できます。

5. ローカルサーバーを起動して動かしてみよう!

作成したAPIをローカル環境で動かしてみましょう。以下のコマンドを実行します。

npm run dev

ターミナルに http://localhost:8787 のようなURLが表示されたら準備完了です!

ブラウザで http://localhost:8787/api/todos にアクセスしてみてください。 以下のようなJSONデータが表示されれば、GET(一覧取得)APIの作成成功です!

[
  { "id": "1", "title": "部屋の掃除をする", "completed": false },
  { "id": "2", "title": "Reactの勉強をする", "completed": true }
]

6. ターミナルコマンドからCRUD操作をしてみよう

簡単にターミナルコマンドを使ってAPI操作をしてみましょう。
mac linuxのコマンドは以下の curl コマンドで、windowsの方はさらに下で解説するInvoke-RestMethodコマンドで行います。

【追加 (CREATE)】新しいToDoを追加する (POST)

-X POST でリクエストを送信し、-H で「JSONデータを送りますよ」と伝え、-d に送信したいJSONデータを渡します。

curl -X POST http://localhost:8787/api/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "ターミナルからテスト追加"}'

実行して以下のようにID付きのJSONが返ってくれば成功です!

{"id":"1712345678900","title":"ターミナルからテスト追加","completed":false}

【削除 (DELETE)】指定したIDのToDoを削除する (DELETE)

-X DELETE を指定し、URLの末尾に削除したいToDoの id を付けて実行します。

例えば、初期データにある id: “1”(部屋の掃除をする)を削除する場合はこちらです。

curl -X DELETE http://localhost:8787/api/todos/1

成功した時の返り値(レスポンス)はこちら

{"message":"削除が完了しました"}

【更新 (UPDATE)】完了フラグを true に切り替える (PUT)

例えば id: “2” のToDoを「完了(completed: true)」に変更する場合:

curl -X PUT http://localhost:8787/api/todos/2 \
  -H "Content-Type: application/json" \
  -d '{"completed": true}'

📝 PowerShell(Windows標準)を使う場合の注意点

Windowsの標準 PowerShell をお使いの場合、curl は内部で別のコマンド(Invoke-WebRequest)の別名になっていることがあり、上記の書き方だとエラーになる場合があります。

PowerShellで実行する場合は、以下のように Invoke-RestMethod(インボーク・レスト・メソッド)で行うと確実に動作します。

PowerShell用コマンド例(POST追加)

Invoke-RestMethod(インボーク・レスト・メソッド)は、Windowsの標準ターミナル(PowerShell)に最初から組み込まれている「Web APIと通信するための標準コマンド(コマンドレット)」です。​

LinuxやMacでよく使われる curl のPowerShell版のような存在で、WebサーバーやWeb APIに対してデータの要求(GET)や送信(POST/PUT/DELETE)を行うことができます。

Invoke-RestMethod -Uri "http://localhost:8787/api/todos" -Method Post -ContentType "application/json" -Body '{"title":"PowerShellから追加"}'

PowerShell用コマンド例(DELETE削除)

Invoke-RestMethod -Uri "http://localhost:8787/api/todos/1" -Method Delete

まとめと次回予告

今回は Hono を使って、ToDoアプリに必要な CRUD (Create / Read / Update / Delete) のAPI基盤を作成しました。

Honoのシンプルさと、Cloudflare Workersの起動速度の速さを体感していただけたのではないでしょうか?

ただし、現在の実装だと「ローカルサーバーを再起動すると、追加したToDoが消えてしまう(インメモリデータのため)」 という問題があります。

そこで次回・第2弾では、Cloudflareが提供するサーバーレス分散データベース 「Cloudflare D1」 を導入し、データをデータベースへ永久に保存できるようにアップデートしていきます!お楽しみに!

コメント