mcpField
H
hono-routing

hono-routing

github.com/jackspace開発1645 回インストール13 週間前に更新
リポジトリ

hono-routing について

hono-routingはHono開発者向けのルーティングとミドルウェアのスキルです。複数のJavaScriptランタイムで使える、TypeScriptによる型付きルート、リクエスト検証、RPC、エラー処理の実践ガイドを提供します。

主な機能

  1. 01ルートとサブアプリの整理
  2. 02組み込みおよび独自ミドルウェアの構成
  3. 03型付きコンテキストの拡張
  4. 04複数のライブラリによるリクエスト検証
  5. 05RPCによるクライアント型の推論
  6. 06一元的なエラー処理の設定

活用シーン

  1. 01Honoアプリのルート構成の設計
  2. 02リクエストパラメーターやフォームデータの検証
  3. 03型安全なクライアント・サーバー間呼び出しの構築
  4. 04HTTPエラーと未定義ルートの一貫した処理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jackspace/claudeskillz --skill hono-routing

インストール後、Claude Code で/hono-routingを入力して読み込まれたか確認します。

同梱ファイルREADME.mdSKILL.json

詳細

Hono のルーティングとミドルウェア

ステータス:本番環境対応 ✅ 最終更新日:2025-10-22 依存関係:なし(フレームワーク非依存) 最新バージョン:[email protected]、[email protected]、[email protected]、@hono/[email protected]、@hono/[email protected]


クイックスタート(15 分)

1. Hono をインストールする
npm install [email protected]

Hono を選ぶ理由:

  • 高速:Web 標準を基盤とし、あらゆる JavaScript ランタイムで動作します
  • 軽量:約 10KB で、依存関係はありません
  • 型安全:型推論に対応した完全な TypeScript サポート
  • 柔軟:Cloudflare Workers、Deno、Bun、Node.js、Vercel で動作します
2. 基本的なアプリを作成する
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => {
  return c.json({ message: 'Hello Hono!' })
})

export default app

重要:

  • レスポンスには c.json()、c.text()、c.html() を使用します
  • レスポンスを返します(Express のように res.send() は使用しません)
  • ランタイム(Cloudflare Workers、Deno、Bun、Node.js)向けにアプリをエクスポートします
3. リクエストのバリデーションを追加する
npm install [email protected] @hono/[email protected]
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const schema = z.object({
  name: z.string(),
  age: z.number(),
})

app.post('/user', zValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  return c.json({ success: true, data })
})

バリデーションを行う理由:

  • リクエストデータの型安全性を確保できます
  • エラーレスポンスを自動で返します
  • TypeScript だけでなく、実行時にもバリデーションを行います

Hono 習得ガイド:6 つのパート

パート 1:ルーティングパターン
基本的なルート
import { Hono } from 'hono'

const app = new Hono()

// GET request
app.get('/posts', (c) => c.json({ posts: [] }))

// POST request
app.post('/posts', (c) => c.json({ created: true }))

// PUT request
app.put('/posts/:id', (c) => c.json({ updated: true }))

// DELETE request
app.delete('/posts/:id', (c) => c.json({ deleted: true }))

// Multiple methods
app.on(['GET', 'POST'], '/multi', (c) => c.text('GET or POST'))

// All methods
app.all('/catch-all', (c) => c.text('Any method'))

要点:

  • 常に Response を返します(c.json、c.text、c.html など)
  • ルートは定義された順に照合されます(最初に一致したものが優先されます)
  • ワイルドカードルートより前に、具体的なルートを定義します
ルートパラメーター
// Single parameter
app.get('/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ userId: id })
})

// Multiple parameters
app.get('/posts/:postId/comments/:commentId', (c) => {
  const { postId, commentId } = c.req.param()
  return c.json({ postId, commentId })
})

// Optional parameters (using wildcards)
app.get('/files/*', (c) => {
  const path = c.req.param('*')
  return c.json({ filePath: path })
})

重要:

  • c.req.param('name') は単一のパラメーターを返します
  • c.req.param() はすべてのパラメーターをオブジェクトとして返します
  • パラメーターは常に文字列です(必要に応じて数値に変換します)
クエリパラメーター
app.get('/search', (c) => {
  // Single query param
  const q = c.req.query('q')

  // Multiple query params
  const { page, limit } = c.req.query()

  // Query param array (e.g., ?tag=js&tag=ts)
  const tags = c.req.queries('tag')

  return c.json({ q, page, limit, tags })
})

ベストプラクティス:

  • クエリパラメーターにはバリデーションを使用します(パート 4 を参照)
  • 省略可能なパラメーターにはデフォルト値を設定します
  • クエリー文字列から数値やブール値を解析します
ワイルドカードルート
// Match any path after /api/
app.get('/api/*', (c) => {
  const path = c.req.param('*')
  return c.json({ catchAll: path })
})

// Named wildcard
app.get('/files/:filepath{.+}', (c) => {
  const filepath = c.req.param('filepath')
  return c.json({ file: filepath })
})
ルートのグループ化(サブアプリ)
// Create sub-app
const api = new Hono()

api.get('/users', (c) => c.json({ users: [] }))
api.get('/posts', (c) => c.json({ posts: [] }))

// Mount sub-app
const app = new Hono()
app.route('/api', api)

// Result: /api/users, /api/posts

ルートをグループ化する理由:

  • 大規模なアプリケーションを整理できます
  • 特定のルート間でミドルウェアを共有できます
  • コードの構成と保守性が向上します

パート 2:ミドルウェアの合成
ミドルウェアのフロー
import { Hono } from 'hono'

const app = new Hono()

// Global middleware (runs for all routes)
app.use('*', async (c, next) => {
  console.log(`[${c.req.method}] ${c.req.url}`)
  await next() // CRITICAL: Must call next()
  console.log('Response sent')
})

// Route-specific middleware
app.use('/admin/*', async (c, next) => {
  // Auth check
  const token = c.req.header('Authorization')
  if (!token) {
    return c.json({ error: 'Unauthorized' }, 401)
  }
  await next()
})

app.get('/admin/dashboard', (c) => {
  return c.json({ message: 'Admin Dashboard' })
})

重要:

  • ミドルウェア内では、必ず await next() を呼び出します
  • ミドルウェアはハンドラーの前に実行されます
  • ハンドラーの実行を防ぐには、早期にリターンします
  • エラーを処理するには、next() の後に c.error を確認します
組み込みミドルウェア
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
import { prettyJSON } from 'hono/pretty-json'
import { compress } from 'hono/compress'
import { cache } from 'hono/cache'

const app = new Hono()

// Request logging
app.use('*', logger())

// CORS
app.use('/api/*', cors({
  origin: 'https://example.com',
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowHeaders: ['Content-Type', 'Authorization'],
}))

// Pretty JSON (dev only)
app.use('*', prettyJSON())

// Compression (gzip/deflate)
app.use('*', compress())

// Cache responses
app.use(
  '/static/*',
  cache({
    cacheName: 'my-app',
    cacheControl: 'max-age=3600',
  })
)

組み込みミドルウェアのリファレンス:references/middleware-catalog.md を参照してください

ミドルウェアの連結
// Multiple middleware in sequence
app.get(
  '/protected',
  authMiddleware,
  rateLimitMiddleware,
  (c) => {
    return c.json({ data: 'Protected data' })
  }
)

// Middleware factory pattern
const authMiddleware = async (c, next) => {
  const token = c.req.header('Authorization')
  if (!token) {
    throw new HTTPException(401, { message: 'Unauthorized' })
  }

  // Set user in context
  c.set('user', { id: 1, name: 'Alice' })

  await next()
}

const rateLimitMiddleware = async (c, next) => {
  // Rate limit logic
  await next()
}

ミドルウェアを連結する理由:

  • 関心事を分離できます
  • 複数のルートで再利用できます
  • 実行順序が明確になります
カスタムミドルウェア
// Timing middleware
const timing = async (c, next) => {
  const start = Date.now()
  await next()
  const elapsed = Date.now() - start
  c.res.headers.set('X-Response-Time', `${elapsed}ms`)
}

// Request ID middleware
const requestId = async (c, next) => {
  const id = crypto.randomUUID()
  c.set('requestId', id)
  await next()
  c.res.headers.set('X-Request-ID', id)
}

// Error logging middleware
const errorLogger = async (c, next) => {
  await next()
  if (c.error) {
    console.error('Error:', c.error)
    // Send to error tracking service
  }
}

app.use('*', timing)
app.use('*', requestId)
app.use('*', errorLogger)

ベストプラクティス:

  • ミドルウェアの役割を絞ります(単一責任の原則)
  • ミドルウェア間でデータを共有するには c.set() を使用します
  • エラーを処理するには、next() の後に c.error を確認します
  • 実行を中断するには、早期にリターンします

パート 3:型安全なコンテキストの拡張
c.set() と c.get() の使用
import { Hono } from 'hono'

type Bindings = {
  DATABASE_URL: string
}

type Variables = {
  user: {
    id: number
    name: string
  }
  requestId: string
}

const app = new Hono<{ Bindings: Bindings; Variables: Variables }>()

// Middleware sets variables
app.use('*', async (c, next) => {
  c.set('requestId', crypto.randomUUID())
  await next()
})

app.use('/api/*', async (c, next) => {
  c.set('user', { id: 1, name: 'Alice' })
  await next()
})

// Route accesses variables
app.get('/api/profile', (c) => {
  const user = c.get('user') // Type-safe!
  const requestId = c.get('requestId') // Type-safe!

  return c.json({ user, requestId })
})

重要:

  • 型安全な c.get() のために Variables 型を定義します
  • 環境変数(Cloudflare Workers)のために Bindings 型を定義します
  • ミドルウェアでは c.set()、ハンドラーでは c.get() を使用します
カスタムコンテキストの拡張
import { Hono } from 'hono'
import type { Context } from 'hono'

type Env = {
  Variables: {
    logger: {
      info: (message: string) => void
      error: (message: string) => void
    }
  }
}

const app = new Hono<Env>()

// Create logger middleware
app.use('*', async (c, next) => {
  const logger = {
    info: (msg: string) => console.log(`[INFO] ${msg}`),
    error: (msg: string) => console.error(`[ERROR] ${msg}`),
  }

  c.set('logger', logger)
  await next()
})

app.get('/', (c) => {
  const logger = c.get('logger')
  logger.info('Hello from route')

  return c.json({ message: 'Hello' })
})

高度なパターン:templates/context-extension.ts を参照してください


パート 4:リクエストのバリデーション
Zod によるバリデーション
npm install [email protected] @hono/[email protected]
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

// Define schema
const userSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().min(18).optional(),
})

// Validate JSON body
app.post('/users', zValidator('json', userSchema), (c) => {
  const data = c.req.valid('json') // Type-safe!
  return c.json({ success: true, data })
})

// Validate query params
const searchSchema = z.object({
  q: z.string(),
  page: z.string().transform((val) => parseInt(val, 10)),
  limit: z.string().transform((val) => parseInt(val, 10)).optional(),
})

app.get('/search', zValidator('query', searchSchema), (c) => {
  const { q, page, limit } = c.req.valid('query')
  return c.json({ q, page, limit })
})

// Validate route params
const idSchema = z.object({
  id: z.string().uuid(),
})

app.get('/users/:id', zValidator('param', idSchema), (c) => {
  const { id } = c.req.valid('param')
  return c.json({ userId: id })
})

// Validate headers
const headerSchema = z.object({
  'authorization': z.string().startsWith('Bearer '),
  'content-type': z.string(),
})

app.post('/auth', zValidator('header', headerSchema), (c) => {
  const headers = c.req.valid('header')
  return c.json({ authenticated: true })
})

重要:

  • バリデーション後は、必ず c.req.valid() を使用します(型安全です)
  • バリデーション対象:json、query、param、header、form、cookie
  • 文字列を数値や日付に変換するには z.transform() を使用します
  • バリデーションエラーが発生すると、自動的に 400 を返します
カスタムバリデーションフック
import { zValidator } from '@hono/zod-validator'
import { HTTPException } from 'hono/http-exception'

const schema = z.object({
  name: z.string(),
  age: z.number(),
})

// Custom error handler
app.post(
  '/users',
  zValidator('json', schema, (result, c) => {
    if (!result.success) {
      // Custom error response
      return c.json(
        {
          error: 'Validation failed',
          issues: result.error.issues,
        },
        400
      )
    }
  }),
  (c) => {
    const data = c.req.valid('json')
    return c.json({ success: true, data })
  }
)

// Throw HTTPException
app.post(
  '/users',
  zValidator('json', schema, (result, c) => {
    if (!result.success) {
      throw new HTTPException(400, { cause: result.error })
    }
  }),
  (c) => {
    const data = c.req.valid('json')
    return c.json({ success: true, data })
  }
)
Valibot によるバリデーション
npm install [email protected] @hono/[email protected]
import { vValidator } from '@hono/valibot-validator'
import * as v from 'valibot'

const schema = v.object({
  name: v.string(),
  age: v.number(),
})

app.post('/users', vValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  return c.json({ success: true, data })
})

Zod と Valibot の比較:references/validation-libraries.md を参照してください

Typia によるバリデーション
npm install typia @hono/[email protected]
import { typiaValidator } from '@hono/typia-validator'
import typia from 'typia'

interface User {
  name: string
  age: number
}

const validate = typia.createValidate<User>()

app.post('/users', typiaValidator('json', validate), (c) => {
  const data = c.req.valid('json')
  return c.json({ success: true, data })
})

Typia を選ぶ理由:

  • 最速のバリデーション(コンパイル時)
  • 実行時のスキーマ定義が不要
  • AOT(Ahead-of-Time)コンパイル
ArkType によるバリデーション
npm install arktype @hono/[email protected]
import { arktypeValidator } from '@hono/arktype-validator'
import { type } from 'arktype'

const schema = type({
  name: 'string',
  age: 'number',
})

app.post('/users', arktypeValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  return c.json({ success: true, data })
})

比較:詳しい比較については references/validation-libraries.md を参照してください


パート 5:型付きルート(RPC)
RPC を使う理由

Hono の RPC 機能を使うと、API の型を手動で定義せずに、型安全なクライアント/サーバー間通信を実現できます。クライアントはサーバーのルートから型を直接推論します。

サーバー側のセットアップ
// app.ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()

const schema = z.object({
  name: z.string(),
  age: z.number(),
})

// Define route and export type
const route = app.post(
  '/users',
  zValidator('json', schema),
  (c) => {
    const data = c.req.valid('json')
    return c.json({ success: true, data }, 201)
  }
)

// Export app type for RPC client
export type AppType = typeof route

// OR export entire app
// export type AppType = typeof app

export default app

重要:

  • RPC の型推論には const route = app.get(...) を必ず使用してください
  • typeof route または typeof app をエクスポートしてください
  • 匿名のルート定義は使用しないでください
クライアント側のセットアップ
// client.ts
import { hc } from 'hono/client'
import type { AppType } from './app'

const client = hc<AppType>('http://localhost:8787')

// Type-safe API call
const res = await client.users.$post({
  json: {
    name: 'Alice',
    age: 30,
  },
})

// Response is typed!
const data = await res.json() // { success: boolean, data: { name: string, age: number } }

RPC を使用する理由:

  • ✅ 完全な型推論(リクエスト+レスポンス)
  • ✅ 型定義を手動で行う必要がありません
  • ✅ コンパイル時のエラーチェック
  • ✅ IDE での自動補完
複数のルートを使った RPC
// Server
const app = new Hono()

const getUsers = app.get('/users', (c) => {
  return c.json({ users: [] })
})

const createUser = app.post(
  '/users',
  zValidator('json', userSchema),
  (c) => {
    const data = c.req.valid('json')
    return c.json({ success: true, data }, 201)
  }
)

const getUser = app.get('/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, name: 'Alice' })
})

// Export combined type
export type AppType = typeof getUsers | typeof createUser | typeof getUser

// Client
const client = hc<AppType>('http://localhost:8787')

// GET /users
const usersRes = await client.users.$get()

// POST /users
const createRes = await client.users.$post({
  json: { name: 'Alice', age: 30 },
})

// GET /users/:id
const userRes = await client.users[':id'].$get({
  param: { id: '123' },
})
RPC のパフォーマンス最適化

問題: 多数のルートがある大規模なアプリでは、型推論が遅くなります

解決策: アプリ全体ではなく、特定のルートグループをエクスポートします

// ❌ Slow: Export entire app
export type AppType = typeof app

// ✅ Fast: Export specific routes
const userRoutes = app.get('/users', ...).post('/users', ...)
export type UserRoutes = typeof userRoutes

const postRoutes = app.get('/posts', ...).post('/posts', ...)
export type PostRoutes = typeof postRoutes

// Client imports specific routes
import type { UserRoutes } from './app'
const userClient = hc<UserRoutes>('http://localhost:8787')

詳しくは: references/rpc-guide.md を参照してください


パート 6:エラー処理
HTTPException
import { Hono } from 'hono'
import { HTTPException } from 'hono/http-exception'

const app = new Hono()

app.get('/users/:id', (c) => {
  const id = c.req.param('id')

  // Throw HTTPException for client errors
  if (!id) {
    throw new HTTPException(400, { message: 'ID is required' })
  }

  // With custom response
  if (id === 'invalid') {
    const res = new Response('Custom error body', { status: 400 })
    throw new HTTPException(400, { res })
  }

  return c.json({ id })
})

重要:

  • 想定されるエラー(400、401、403、404)には HTTPException を使用してください
  • 想定外のエラー(500)には使用せず、代わりに onError を使用してください
  • HTTPException は実行を直ちに停止します
グローバルエラーハンドラー(onError)
import { Hono } from 'hono'
import { HTTPException } from 'hono/http-exception'

const app = new Hono()

// Custom error handler
app.onError((err, c) => {
  // Handle HTTPException
  if (err instanceof HTTPException) {
    return err.getResponse()
  }

  // Handle unexpected errors
  console.error('Unexpected error:', err)

  return c.json(
    {
      error: 'Internal Server Error',
      message: err.message,
    },
    500
  )
})

app.get('/error', (c) => {
  throw new Error('Something went wrong!')
})

onError を使用する理由:

  • エラー処理を一元化できます
  • 一貫性のあるエラーレスポンスを返せます
  • エラーのログ記録と追跡ができます
ミドルウェアでのエラーチェック
app.use('*', async (c, next) => {
  await next()

  // Check for errors after handler
  if (c.error) {
    console.error('Error in route:', c.error)
    // Send to error tracking service
  }
})
Not Found ハンドラー
app.notFound((c) => {
  return c.json({ error: 'Not Found' }, 404)
})
エラー処理のベストプラクティス
import { Hono } from 'hono'
import { HTTPException } from 'hono/http-exception'

const app = new Hono()

// Validation errors
app.post('/users', zValidator('json', schema), (c) => {
  // zValidator automatically returns 400 on validation failure
  const data = c.req.valid('json')
  return c.json({ data })
})

// Authorization errors
app.use('/admin/*', async (c, next) => {
  const token = c.req.header('Authorization')
  if (!token) {
    throw new HTTPException(401, { message: 'Unauthorized' })
  }
  await next()
})

// Not found errors
app.get('/users/:id', async (c) => {
  const id = c.req.param('id')
  const user = await db.getUser(id)

  if (!user) {
    throw new HTTPException(404, { message: 'User not found' })
  }

  return c.json({ user })
})

// Server errors
app.get('/data', async (c) => {
  try {
    const data = await fetchExternalAPI()
    return c.json({ data })
  } catch (error) {
    // Let onError handle it
    throw error
  }
})

// Global error handler
app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return err.getResponse()
  }

  console.error('Unexpected error:', err)
  return c.json({ error: 'Internal Server Error' }, 500)
})

// 404 handler
app.notFound((c) => {
  return c.json({ error: 'Not Found' }, 404)
})

重要なルール

必ず行うこと

✅ ミドルウェアで await next() を呼び出す - ミドルウェアチェーンの実行に必要です ✅ ハンドラーから Response を返す - c.json()、c.text()、c.html() を使用してください ✅ バリデーション後に c.req.valid() を使用する - 型安全な検証済みデータを取得できます ✅ RPC 用のルート型をエクスポートする - export type AppType = typeof route ✅ クライアントエラーには HTTPException をスローする - 400、401、403、404 エラーに使用します ✅ グローバルなエラー処理には onError を使用する - エラーレスポンスを一元化できます ✅ c.set/c.get 用の Variables 型を定義する - コンテキスト変数の型安全性を確保できます ✅ const route = app.get(...) を使用する - RPC の型推論に必要です

絶対に行わないこと

❌ ミドルウェアで await next() を呼び忘れる - ミドルウェアチェーンが機能しなくなります ❌ Express のように res.send() を使用する - Hono とは互換性がありません ❌ バリデーションせずにリクエストデータへアクセスする - 型安全性のため、バリデーターを使用してください ❌ 大規模な RPC でアプリ全体をエクスポートする - 型推論が遅くなるため、特定のルートをエクスポートしてください ❌ 単純な throw new Error() を使用する - 代わりに HTTPException を使用してください ❌ onError ハンドラーを省略する - エラーレスポンスに一貫性がなくなります ❌ Variables 型を指定せずに c.set/c.get を使用する - 型安全性が失われます


既知の問題の防止

このスキルは、文書化されている 8 件の問題を防止します。

問題 #1:RPC の型推論が遅い

エラー: ルートが多いと IDE の動作が遅くなります 出典: hono/docs/guides/rpc 発生する理由: 多数のルートを持つ typeof app から複雑な型インスタンス化が行われます 防止策: アプリ全体ではなく、特定のルートグループをエクスポートします

// ❌ Slow
export type AppType = typeof app

// ✅ Fast
const userRoutes = app.get(...).post(...)
export type UserRoutes = typeof userRoutes
問題 #2:RPC でミドルウェアのレスポンスが型推論されない

エラー: RPC クライアントがミドルウェアのレスポンスを推論しません 出典: honojs/hono#2719 発生する理由: RPC モードでは、デフォルトでミドルウェアのレスポンスが推論されません 防止策: ミドルウェアを含む特定のルート型をエクスポートします

const route = app.get(
  '/data',
  myMiddleware,
  (c) => c.json({ data: 'value' })
)
export type AppType = typeof route
問題 #3:バリデーションフックの混乱

エラー: バリデーターライブラリによってフックのパターンが異なります 出典: Context7 の調査 発生する理由: 各バリデーター(@hono/zod-validator、@hono/valibot-validator など)で API が若干異なります 防止策: このスキルでは、すべてのバリデーターに共通するパターンを提供します

問題 #4:HTTPException の誤用

エラー: HTTPException ではなく、通常の Error をスローしています 出典: 公式ドキュメント 発生する理由: Express に慣れた開発者が throw new Error() を使用します 防止策: クライアントエラー(400~499)には必ず HTTPException を使用してください

// ❌ Wrong
throw new Error('Unauthorized')

// ✅ Correct
throw new HTTPException(401, { message: 'Unauthorized' })
問題 #5:コンテキストの型安全性が失われる

エラー: 型推論なしで c.set() と c.get() を使用しています 出典: 公式ドキュメント 発生する理由: Hono のジェネリックで Variables 型を定義していません 防止策: 必ず Variables 型を定義してください

type Variables = {
  user: { id: number; name: string }
}

const app = new Hono<{ Variables: Variables }>()
Issue #6: ミドルウェアの後のエラーチェック漏れ

エラー:ハンドラー内のエラーが捕捉されません ソース:公式ドキュメント 原因:await next() の後に c.error を確認していません 防止策:ミドルウェア内で c.error を確認します

app.use('*', async (c, next) => {
  await next()
  if (c.error) {
    console.error('Error:', c.error)
  }
})
Issue #7: 検証せずにリクエストへ直接アクセス

エラー:検証せずに c.req.param() または c.req.query() にアクセスしています ソース:ベストプラクティス 原因:開発者が手早く済ませるために検証を省略しています 防止策:必ずバリデーターと c.req.valid() を使用します

// ❌ Wrong
const id = c.req.param('id') // string, no validation

// ✅ Correct
app.get('/users/:id', zValidator('param', idSchema), (c) => {
  const { id } = c.req.valid('param') // validated UUID
})
Issue #8: ミドルウェアの順序が正しくない

エラー:ミドルウェアが誤った順序で実行されています ソース:公式ドキュメント 原因:ミドルウェアチェーンの実行順序を誤解しています 防止策:ミドルウェアは上から下へ実行され、await next() がハンドラーを実行した後、下から上へ戻ることを覚えておきます

app.use('*', async (c, next) => {
  console.log('1: Before handler')
  await next()
  console.log('4: After handler')
})

app.use('*', async (c, next) => {
  console.log('2: Before handler')
  await next()
  console.log('3: After handler')
})

app.get('/', (c) => {
  console.log('Handler')
  return c.json({})
})

// Output: 1, 2, Handler, 3, 4

設定ファイルのリファレンス

package.json(完全な例)
{
  "name": "hono-app",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js"
  },
  "dependencies": {
    "hono": "^4.10.2"
  },
  "devDependencies": {
    "typescript": "^5.9.0",
    "tsx": "^4.19.0",
    "@types/node": "^22.10.0"
  }
}
検証機能付き package.json(Zod)
{
  "dependencies": {
    "hono": "^4.10.2",
    "zod": "^4.1.12",
    "@hono/zod-validator": "^0.7.4"
  }
}
検証機能付き package.json(Valibot)
{
  "dependencies": {
    "hono": "^4.10.2",
    "valibot": "^1.1.0",
    "@hono/valibot-validator": "^0.5.3"
  }
}
すべてのバリデーターを含む package.json
{
  "dependencies": {
    "hono": "^4.10.2",
    "zod": "^4.1.12",
    "valibot": "^1.1.0",
    "@hono/zod-validator": "^0.7.4",
    "@hono/valibot-validator": "^0.5.3",
    "@hono/typia-validator": "^0.1.2",
    "@hono/arktype-validator": "^2.0.1"
  }
}
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "lib": ["ES2022"],
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "allowJs": true,
    "checkJs": false,
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

ファイルテンプレート

すべてのテンプレートは templates/ ディレクトリにあります。

  • routing-patterns.ts - ルートパラメーター、クエリパラメーター、ワイルドカード、グループ化
  • middleware-composition.ts - ミドルウェアのチェーン、組み込みミドルウェア
  • validation-zod.ts - カスタムフックを使った Zod 検証
  • validation-valibot.ts - Valibot 検証
  • rpc-pattern.ts - 型安全な RPC クライアント/サーバー
  • error-handling.ts - HTTPException、onError、カスタムエラー
  • context-extension.ts - c.set/c.get、カスタムコンテキスト型
  • package.json - すべての依存関係

これらのファイルをプロジェクトにコピーし、必要に応じてカスタマイズしてください。


リファレンスドキュメント

詳しくは、以下を参照してください。

  • middleware-catalog.md - Hono の組み込みミドルウェア完全リファレンス
  • validation-libraries.md - Zod、Valibot、Typia、ArkType の比較
  • rpc-guide.md - RPC パターンの詳細解説、パフォーマンスの最適化
  • top-errors.md - Hono でよくあるエラーと解決策

公式ドキュメント


依存関係(最終確認日:2025-10-22)

{
  "dependencies": {
    "hono": "^4.10.2"
  },
  "optionalDependencies": {
    "zod": "^4.1.12",
    "valibot": "^1.1.0",
    "@hono/zod-validator": "^0.7.4",
    "@hono/valibot-validator": "^0.5.3",
    "@hono/typia-validator": "^0.1.2",
    "@hono/arktype-validator": "^2.0.1"
  },
  "devDependencies": {
    "typescript": "^5.9.0"
  }
}

本番環境での例

このスキルは、複数のランタイム環境で検証されています。

  • Cloudflare Workers:ルーティング、ミドルウェア、RPC パターン
  • Deno:すべての検証ライブラリをテスト済み
  • Bun:パフォーマンスベンチマークを完了
  • Node.js:すべてのテストスイートに合格

このスキルのすべてのパターンは、本番環境で検証されています。


質問や問題がありますか?

  1. まず references/top-errors.md を確認します
  2. セットアップ手順をすべて確認します
  3. ミドルウェア内で await next() が呼び出されていることを確認します
  4. RPC ルートで const route = app.get(...) パターンが使われていることを確認します
  5. 公式ドキュメントを確認します:https://hono.dev

コメント