Hono エンドポイントの追加¶
Hono REST テンプレート (および Full-Stack / Monorepo 派生) はユーザー CRUDを同梱しています。このレシピでは、ルートハンドラ、Drizzle による永続化、Zod バリデーション、OpenAPI ドキュメントまで、新しいエンドポイントをエンドツーエンドで追加する手順を説明します。
デフォルトで提供されるもの¶
生成されたプロジェクトには以下が既に組み込まれています:
src/Server.res— ロガー、/openapi.json、/docs(Scalar UI) を備えた Hono アプリsrc/Routes/Users.res—/usersの GET/POST/PUT/DELETEsrc/Schema.res— Drizzle のusersテーブルsrc/Db.res— libsql クライアント + 汎用クエリヘルパーsrc/ZodOpenapi.res—@hono/zod-openapi向けの Zod バインディング
目標: 投稿一覧を返し、投稿を作成する /posts エンドポイントを追加します。
Step 1 — Schema.res にテーブルを追加する¶
let posts = sqliteTable("posts", {
"id": intCol("id", {"primaryKey": true, "autoIncrement": true}),
"title": textCol("title", {"notNull": true}),
"body": textCol("body", {"notNull": true}),
"authorId": intCol("author_id", {"notNull": true}),
})
マイグレーションを生成して適用します:
pnpm db:generate
pnpm db:migrate
Step 2 — ルートモジュールを書く¶
src/Routes/Posts.res を作成します:
let register = app => {
app->Hono.get("/posts", async ctx => {
let rows =
await Db.db
->Db.select({
"id": Schema.posts["id"],
"title": Schema.posts["title"],
"body": Schema.posts["body"],
"authorId": Schema.posts["authorId"],
})
->Db.from(Schema.posts)
->Db.allAsync
ctx->Hono.json(rows)
})
app->Hono.post("/posts", async ctx => {
let payload = await ctx->Hono.req->Hono.jsonBody
let inserted =
await Db.db
->Db.insert(Schema.posts)
->Db.values({
"title": payload["title"],
"body": payload["body"],
"authorId": payload["authorId"],
})
->Db.returning
ctx->Hono.status(201)->Hono.json(inserted->Array.get(0))
})
}
Step 3 — Server.res に登録する¶
Routes.Users.register(app)
Routes.Posts.register(app)
Step 4 — Zod バリデーションを追加する¶
ペイロードの形状を共有の Zod スキーマとして切り出します:
let createPostSchema = ZodOpenapi.object({
"title": ZodOpenapi.string(~minLength=1, ()),
"body": ZodOpenapi.string(),
"authorId": ZodOpenapi.number(),
})
データベースにアクセスする前にバリデーションを実行します:
app->Hono.post("/posts", async ctx => {
let raw = await ctx->Hono.req->Hono.jsonBody
switch createPostSchema->ZodOpenapi.safeParse(raw) {
| {success: true, data} =>
/* insert data */
| {success: false, error} =>
ctx->Hono.status(400)->Hono.json({"error": error})
}
})
Step 5 — OpenAPI に公開する¶
テンプレートには /openapi.json と /docs が既に配線されています。app->Hono.get(...) で登録したルートは自動的に表示されます。より詳細なメタデータ (タグ、レスポンススキーマ) が必要な場合は @hono/zod-openapi の createRoute ヘルパーに切り替えてください。完全なパターンは Hono ドキュメントを参照してください。
Step 6 — テストする¶
# Terminal 1
pnpm dev
# Terminal 2
curl -X POST http://localhost:3000/posts \
-H 'Content-Type: application/json' \
-d '{"title":"Hello","body":"World","authorId":1}'
curl http://localhost:3000/posts
http://localhost:3000/docs を開くと、Scalar UI で新しいエンドポイントを実行できます。
よくある落とし穴¶
マイグレーションの実行漏れ —
db:generateが SQL ファイルを生成し、db:migrateが適用します。insert が "no such table" で失敗する場合は Step 2 を飛ばしています。レコードと dict の混在 — Drizzle の行形状は dict(アクセスは レコードと Dict の混同 — Drizzle の行の形状は Dict (
payload["title"]でアクセス) です。下流でより厳密な型付けが必要であれば、境界で ReScript レコードに変換してください。フロントエンド開発時の CORS — Hono 側で CORS を設定する代わりに、Vite+ の開発プロキシ (Full-Stack / Monorepo テンプレートは自動設定済み) を使用してください。