mcpField
H
hono-routing

hono-routing

github.com/ovachiever開発5656 回インストール49 週間前に更新
リポジトリ

hono-routing について

hono-routing は、Hono でアプリケーションを開発する人向けのフレームワーク非依存のスキルガイドです。ルーティング、ミドルウェア、バリデーション、型安全な RPC、コンテキストの型拡張、エラー処理を扱います。

主な機能

  1. 01ルートパターンとサブアプリ
  2. 02ミドルウェアの構成
  3. 03型安全なコンテキスト拡張
  4. 04複数ライブラリによるバリデーション
  5. 05型安全なクライアント・サーバー RPC
  6. 06一元的なエラー処理

活用シーン

  1. 01Hono アプリのルート整理
  2. 02リクエストデータの実行時検証
  3. 03ミドルウェア間での型付きデータ共有
  4. 04型安全な API クライアントの作成
  5. 05アプリケーションエラーの一貫した処理

インストール

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

$npx skills add https://github.com/ovachiever/droid-tings --skill hono-routing

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
.claude-plugin/
plugin.json
references/
middleware-catalog.md
rpc-guide.md
top-errors.md
validation-libraries.md
scripts/
check-versions.sh
templates/
context-extension.ts
error-handling.ts
middleware-composition.ts
package.json
routing-patterns.ts
rpc-client.ts
rpc-pattern.ts
validation-valibot.ts
validation-zod.ts

詳細

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

コメント