mcpField
H
hono

hono

github.com/honojs開発173.1k 回インストール2 週間前に更新
リポジトリ

hono について

hono は、Hono アプリ開発向けの AI スキルです。API リファレンスを含み、Hono CLI によるアプリの確認とテストを案内します。

主な機能

  1. 01Hono API リファレンスを内蔵
  2. 02最新ドキュメントの取得方法を案内
  3. 03CLI でルートを確認
  4. 04サーバー不要のリクエスト・一括テスト
  5. 05レスポンスの記録と再テスト

活用シーン

  1. 01Hono のルートの作成・変更
  2. 02サーバーを起動せずにエンドポイントをテスト
  3. 03API の動作を記録して変更を検証

インストール

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

$npx skills add https://github.com/honojs/skills --skill hono

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

詳細

Hono スキル

Hono のウェブアプリケーションを構築します。このスキルは、AI 向けに API の知識をインラインで提供します。Hono CLI を使ってアプリを調査、テストしてください。

最新のドキュメント

このインラインリファレンスにない詳細については、https://hono.dev から最新のドキュメントを取得してください。https://hono.dev/llms.txt からドキュメントページのインデックスを取得し、Accept: text/markdown ヘッダーを付けてページを取得すると、Markdown で受け取れます。

curl -H "Accept: text/markdown" https://hono.dev/docs/helpers/cookie

Hono CLI

使い捨てのスクリプトや開発サーバーではなく、Hono CLI で検証してください。これは開発用依存関係です(create-hono から作成したプロジェクトにはすでに含まれています)。app.request() を使ってアプリをプロセス内で読み込み、JSON を出力します。見つからない場合はインストールしてから、ヘルプを確認してください。

npm install -D @hono/cli@next
npx hono --help

冒頭の注記に従ってください。JSON 出力の契約とワークフローについて説明しています。npx hono <command> --help には、各コマンド(routes、request、batch、snapshot、benchmark、ssg、init)の例と注記があります。

注記:

  • npx hono routes はルートを一覧表示し、npx hono request /path は1つのリクエストを送信します。サーバーは不要です。アプリがデフォルトのパス(src/index.ts)にない場合は、すべてのコマンドにファイルを渡してください:npx hono request / src/app.ts。CLI 引数に認証情報を直接渡さないでください。機密情報には環境変数を使用してください。

  • Cloudflare バインディング(KV、D1、R2、vars)は自動的に c.env に渡されます。サーバーもフラグも不要です。cloudflare.config.ts(cf CLI)と Vite 設定があるプロジェクトでは、request、batch、snapshot はデフォルトで Vite 開発サーバー経由で実行され、c.env にはローカルのバインディングが設定されます。src/index.* がない Vite プロジェクト(Vite プラグインで構築されたアプリ)でも、request と batch はデフォルトで Vite を使用します(snapshot には引き続きファイルが必要です)。また、--runtime vite で明示的に選択できます。wrangler 設定があるプロジェクトでは、アプリは実際のローカルバインディングを読み込んで Node.js 上で実行されます。そこで Workers の完全なランタイムを使うには、--runtime workerd を追加してください(ファイル引数は渡さず、エントリポイントは wrangler 設定の main です)。--trace、--watch、およびファイル引数を指定すると、アプリは Node.js 上で実行されます。この場合、cf プロジェクトの c.env は空です。

  • routes と snapshot は、cloudflare:workers をインポートするアプリ(Durable Objects)も読み込みます。ルートの読み込み中は、インポートがスタブに置き換えられます。

  • 既存のディレクトリに Hono アプリを追加するには、npx hono init --template cloudflare-workers(または nodejs、bun、deno など。--template を指定しない場合は、ディレクトリ内のファイルから検出されます)を実行してから、依存関係をインストールしてください。create-hono のテンプレートがコピーされ、既存のファイルが上書きされることはありません。空のディレクトリでは、npm create hono@next を実行しても同様の処理が行われ、プロンプトが表示されます。

  • 複数のリクエストを実行する場合や、状態を保持するフロー(POST してから、返された ID を使用するなど)では、1回の hono batch - 呼び出しで実行してください。1行につき JSON オブジェクトを1つ記述します。値を save し、後続のステップで {{id}} として使用します。ステップ間で同じアプリインスタンスを共有します。各ステップで expect を使って期待するステータスと本文を宣言してください(本文はディープな部分一致です)。概要に "failed": 0 と表示されるまで繰り返します。

    npx hono batch - <<'EOF'
    {"method":"POST","path":"/users","body":{"name":"Alice"},"save":{"id":".id"},"expect":{"status":201}}
    {"path":"/users/{{id}}","expect":{"status":200,"body":{"name":"Alice"}}}
    EOF
    
  • 既存のルートを変更する前に、現在の動作を記録してください:npx hono snapshot は、バッチ JSONL の行として結果を出力します(実際のレスポンスが expect になります)。その行を保存し、変更を加えた後、"failed": 0 になるまで npx hono batch - で再実行してください。

  • 大規模な API では、npx hono snapshot --status-only と npx hono batch - --compact を使用してください。同じ手順で、出力を大幅に小さくできます。失敗したステップには diff が含まれます。そこに示された内容を修正してください。


Hono API リファレンス

アプリコンストラクター
import { Hono } from 'hono'

const app = new Hono()

// With TypeScript generics
type AppEnv = {
  Bindings: Env // generated from the bindings config — see "Environment (Cloudflare Workers)"
  Variables: { user: User }
}
const app = new Hono<AppEnv>()
ルーティングメソッド
app.get('/path', handler)
app.post('/path', handler)
app.put('/path', handler)
app.delete('/path', handler)
app.patch('/path', handler)
app.options('/path', handler)
app.all('/path', handler) // all HTTP methods
app.on('PURGE', '/path', handler) // custom method
app.on(['PUT', 'DELETE'], '/path', handler) // multiple methods
ルーティングパターン
// Path parameters
app.get('/user/:name', (c) => {
  const name = c.req.param('name')
  return c.json({ name })
})

// Multiple params
app.get('/posts/:id/comments/:commentId', (c) => {
  const { id, commentId } = c.req.param()
})

// Optional parameters
app.get('/api/animal/:type?', (c) => c.text('Animal!'))

// Wildcards
app.get('/wild/*/card', (c) => c.text('Wildcard'))

// Regexp constraints
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
  const { date, title } = c.req.param()
})

// Chained routes
app
  .get('/endpoint', (c) => c.text('GET'))
  .post((c) => c.text('POST'))
  .delete((c) => c.text('DELETE'))
ルートのグループ化
// Using route()
const api = new Hono()
api.get('/users', (c) => c.json([]))

const app = new Hono()
app.route('/api', api) // mounts at /api/users

// Using basePath()
const app = new Hono().basePath('/api')
app.get('/users', (c) => c.json([])) // GET /api/users
エラー処理
app.notFound((c) => c.json({ message: 'Not Found' }, 404))

app.onError((err, c) => {
  console.error(err)
  return c.json({ message: 'Internal Server Error' }, 500)
})

コンテキスト(c)

レスポンスメソッド
c.text('Hello') // text/plain
c.json({ message: 'Hello' }) // application/json
c.html('<h1>Hello</h1>') // text/html
c.redirect('/new-path') // 302 redirect
c.redirect('/new-path', 301) // 301 redirect
c.body('raw body', 200, headers) // raw response
c.notFound() // 404 response
ヘッダーとステータス
c.status(201)
c.header('X-Custom', 'value')
c.header('Cache-Control', 'no-store')
変数(リクエスト単位のデータ)
// In middleware
c.set('user', { id: 1, name: 'Alice' })

// In handler
const user = c.get('user')
// or
const user = c.var.user
環境(Cloudflare Workers)

Bindings 型を手書きしてはいけません。生成し、バインディングを変更するたびに再実行してください。プロジェクトがどのツールを使っているかは、ファイルから確認できます。

cf CLI プロジェクト(cloudflare.config.ts。現在、create-hono が cloudflare-workers 向けに生成する形式です):バインディングは、cf/config の bindings ヘルパーを使って cloudflare.config.ts で宣言します。ローカルバインディングには ID は不要です。ユーザーから依頼されない限り、Cloudflare リソース(KV、D1、R2)を作成したり、ID を設定したりしないでください。その後、npm run typecheck(= cf workers types && tsc。.cloudflare/types に書き込みます)を実行し、生成された Env を使用します。

// cloudflare.config.ts
import { bindings, defineConfig } from 'cf/config'
import * as entrypoint from './src/index.ts' with { type: 'cf-worker' }

export default defineConfig({
  worker: {
    name: 'my-app',
    compatibilityDate: '2026-10-06',
    entrypoint,
    env: {
      MY_KV: bindings.kv(),
      MY_VAR: bindings.text('my-variable'),
    },
  },
})
const app = new Hono<{ Bindings: Env }>()

スクリプトは cf dev、cf build、cf deploy です。この種のプロジェクトには wrangler 設定ファイルがありません。追加しないでください。また、Hono プロジェクトで cf init を実行しないでください(Hono では自動設定が停止します)。

wrangler プロジェクト(wrangler.jsonc または wrangler.toml):バインディングは wrangler 設定ファイルに記述します。npm run cf-typegen(= wrangler types --env-interface CloudflareBindings)を実行すると、Workers のランタイム型とともに CloudflareBindings を宣言する worker-configuration.d.ts が生成されます。cf-typegen スクリプトがない場合は、npx wrangler types を実行し、代わりに宣言されるインターフェース(Env。Cloudflare.Env としても利用できます)を使用してください。@cloudflare/workers-types はどちらの方法でも不要です。インストールしないでください。

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

app.get('/', async (c) => {
  const value = await c.env.MY_KV.get('key')
  const db = c.env.MY_DB
  c.executionCtx.waitUntil(promise)
})
レンダラー
app.use(async (c, next) => {
  c.setRenderer((content) =>
    c.html(
      <html><body>{content}</body></html>
    )
  )
  await next()
})

app.get('/', (c) => c.render(<h1>Hello</h1>))

HonoRequest(c.req)

c.req.param('id') // path parameter
c.req.param() // all path params as object
c.req.query('page') // query string parameter
c.req.query() // all query params; values are string | undefined
c.req.queries('tags') // multiple values: ?tags=A&tags=B → ['A', 'B']
c.req.header('Authorization') // request header
c.req.header() // all headers (keys are lowercase)

// Body parsing
await c.req.json<{ title: string }>() // parse JSON body (unknown without the type argument)
await c.req.text() // parse text body
await c.req.formData() // parse as FormData
await c.req.parseBody() // parse multipart/form-data or urlencoded
await c.req.arrayBuffer() // parse as ArrayBuffer
await c.req.blob() // parse as Blob

// Validated data (used with validator middleware)
c.req.valid('json')
c.req.valid('query')
c.req.valid('form')
c.req.valid('param')

// Properties
c.req.url // full URL string
c.req.path // pathname
c.req.method // HTTP method
c.req.raw // underlying Request object

ミドルウェア

組み込みミドルウェアの使用
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { basicAuth } from 'hono/basic-auth'
import { prettyJSON } from 'hono/pretty-json'
import { secureHeaders } from 'hono/secure-headers'
import { etag } from 'hono/etag'
import { compress } from 'hono/compress'
import { poweredBy } from 'hono/powered-by'
import { timing } from 'hono/timing'
import { cache } from 'hono/cache'
import { bearerAuth } from 'hono/bearer-auth'
import { jwt } from 'hono/jwt'
import { jwk } from 'hono/jwk'
import { csrf } from 'hono/csrf'
import { ipRestriction } from 'hono/ip-restriction'
import { bodyLimit } from 'hono/body-limit'
import { timeout } from 'hono/timeout'
import { requestId } from 'hono/request-id'
import { methodOverride } from 'hono/method-override'
import { methodNotAllowed } from 'hono/method-not-allowed'
import { languageDetector } from 'hono/language'
import { some, every, except } from 'hono/combine'
import { contextStorage, getContext } from 'hono/context-storage'
import { trailingSlash, trimTrailingSlash } from 'hono/trailing-slash'

// Registration
app.use(logger()) // all routes
app.use('/api/*', cors()) // specific path
app.post('/api/*', basicAuth({ username: 'admin', password: 'secret' }))
カスタムミドルウェア
// Inline
app.use(async (c, next) => {
  const start = Date.now()
  await next()
  const elapsed = Date.now() - start
  c.res.headers.set('X-Response-Time', `${elapsed}ms`)
})

// Reusable with createMiddleware
import { createMiddleware } from 'hono/factory'

const auth = createMiddleware(async (c, next) => {
  const token = c.req.header('Authorization')
  if (!token) return c.json({ error: 'Unauthorized' }, 401)
  await next()
})

app.use('/api/*', auth)
ミドルウェアの実行順序

ミドルウェアは登録順に実行されます。await next() は次のミドルウェア/ハンドラーを呼び出し、next() の後のコードは戻る途中で実行されます。

Request → mw1 before → mw2 before → handler → mw2 after → mw1 after → Response
app.use(async (c, next) => {
  // before handler
  await next()
  // after handler
})

バリデーション

バリデーション対象:json、form、query、header、param、cookie。

Zod バリデーター
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const schema = z.object({
  title: z.string().min(1),
  body: z.string()
})

app.post('/posts', zValidator('json', schema), (c) => {
  const data = c.req.valid('json') // fully typed
  return c.json(data, 201)
})
Valibot/Standard Schema バリデーター
import { sValidator } from '@hono/standard-validator'
import * as v from 'valibot'

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

app.post('/users', sValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  return c.json(data, 201)
})

JSX

hono-jsx スキルを使って UI を構築してください(jsxRenderer を使ったレイアウト、Vite、クライアントサイドコード)。このセクションでは構文のみを説明します。

セットアップ

tsconfig.json で設定します。

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "hono/jsx"
  }
}

または、プラグマを使用します:/** @jsxImportSource hono/jsx */

重要: JSX を使うファイルの拡張子は .tsx にする必要があります。.ts を .tsx に変更しないと、コンパイラーでエラーになります。

コンポーネント
import type { PropsWithChildren } from 'hono/jsx'

const Layout = (props: PropsWithChildren) => (
  <html>
    <head>
      <title>My App</title>
    </head>
    <body>{props.children}</body>
  </html>
)

const UserCard = ({ name }: { name: string }) => (
  <div class="card">
    <h2>{name}</h2>
  </div>
)

app.get('/', (c) => {
  return c.html(
    <Layout>
      <UserCard name="Alice" />
    </Layout>
  )
})
jsxRenderer ミドルウェア

レイアウトには jsxRenderer ミドルウェアを使用してください。詳細は https://hono.dev/docs/middleware/builtin/jsx-renderer を参照してください。

非同期コンポーネント
const UserList = async () => {
  const users = await fetchUsers()
  return (
    <ul>
      {users.map((u) => (
        <li>{u.name}</li>
      ))}
    </ul>
  )
}
フラグメント
const Items = () => (
  <>
    <li>Item 1</li>
    <li>Item 2</li>
  </>
)

ストリーミング

import { stream, streamText, streamSSE } from 'hono/streaming'

// Basic stream
app.get('/stream', (c) => {
  return stream(c, async (stream) => {
    stream.onAbort(() => console.log('Aborted'))
    await stream.write(new Uint8Array([0x48, 0x65]))
    await stream.pipe(readableStream)
  })
})

// Text stream
app.get('/stream-text', (c) => {
  return streamText(c, async (stream) => {
    await stream.writeln('Hello')
    await stream.sleep(1000)
    await stream.write('World')
  })
})

// Server-Sent Events
app.get('/sse', (c) => {
  return streamSSE(c, async (stream) => {
    let id = 0
    while (true) {
      await stream.writeSSE({
        data: JSON.stringify({ time: new Date().toISOString() }),
        event: 'time-update',
        id: String(id++)
      })
      await stream.sleep(1000)
    }
  })
})

app.request() を使ったテスト

HTTP サーバーを起動せずにエンドポイントをテストできます。

// GET
const res = await app.request('/posts')
expect(res.status).toBe(200)
expect(await res.json()).toEqual({ posts: [] })

// POST with JSON
const res = await app.request('/posts', {
  method: 'POST',
  body: JSON.stringify({ title: 'Hello' }),
  headers: { 'Content-Type': 'application/json' }
})

// POST with FormData
const formData = new FormData()
formData.append('name', 'Alice')
const res = await app.request('/users', { method: 'POST', body: formData })

// With mock env (Cloudflare Workers bindings)
const res = await app.request('/api/data', {}, { KV: mockKV, DATABASE: mockDB })

// Using Request object
const req = new Request('http://localhost/api', { method: 'DELETE' })
const res = await app.request(req)

Hono クライアント(RPC)

サーバーとクライアントで型を共有する、型安全な API クライアントです。

重要:型推論を機能させるには、ルートをチェーンしなければなりません。チェーンしない場合、クライアントはルート型を推論できません。

// Server: routes MUST be chained to preserve types
const route = app
  .post('/posts', zValidator('json', schema), (c) => {
    return c.json({ ok: true }, 201)
  })
  .get('/posts', (c) => {
    return c.json({ posts: [] })
  })
export type AppType = typeof route

// Client: use hc() with the exported type
import { hc } from 'hono/client'
import type { AppType } from './server'

const client = hc<AppType>('http://localhost:8787/')
const res = await client.posts.$post({ json: { title: 'Hello' } })
const data = await res.json() // fully typed

型ユーティリティ:

import type { InferRequestType, InferResponseType } from 'hono/client'

type ReqType = InferRequestType<typeof client.posts.$post>
type ResType = InferResponseType<typeof client.posts.$post, 200>

ヘルパー

ヘルパーは、hono/<helper-name> からインポートするユーティリティ関数です。

import { getConnInfo } from 'hono/conninfo'
import { getCookie, setCookie, deleteCookie } from 'hono/cookie'
import { css, Style } from 'hono/css'
import { createFactory } from 'hono/factory'
import { html, raw } from 'hono/html'
import { stream, streamText, streamSSE } from 'hono/streaming'
import { testClient } from 'hono/testing'
import { upgradeWebSocket } from '@hono/cloudflare-workers' // or another @hono/<runtime> adapter package

利用可能なヘルパー:Accepts、Adapter、ConnInfo、Cookie、css、Dev、Factory、html、JWT、Proxy、Route、SSG、Streaming、Testing、WebSocket。

詳細は https://hono.dev/docs/helpers/<helper-name> を参照してください(Accept: text/markdown を指定して取得してください)。

Factory

createFactory を使ってアプリの Env 型(上記の AppEnv)を一度定義し、アプリ、ミドルウェア、ハンドラー間で共有します。

import { createFactory } from 'hono/factory'

const factory = createFactory<AppEnv>()

// Create app (AppEnv is inherited)
const app = factory.createApp()

// Create middleware (AppEnv is inherited, no need to pass generics)
const mw = factory.createMiddleware(async (c, next) => {
  await next()
})

// Create handlers separately (preserves type inference)
const handlers = factory.createHandlers(logger(), (c) => c.json({ message: 'Hello' }))
app.get('/api', ...handlers)

ベストプラクティス

  • パスパラメーターを適切に型推論させるため、ハンドラーはルート定義内にインラインで記述してください。
  • 大規模なアプリを機能ごとに整理するには app.route() を使用してください。Rails スタイルのコントローラーは使わないでください。
  • createFactory() を使って、Env 型をアプリ、ミドルウェア、ハンドラー間で共有してください。
  • c.set()/c.get() を使って、ミドルウェアとハンドラー間でデータを受け渡してください。
  • 複数のリクエスト部分(param + query + json)を検証するには、バリデーターをチェーンしてください。
  • RPC 用にアプリの型をエクスポートします:export type AppType = typeof routes
  • テストには app.request() を使用してください。サーバーの起動は不要です。

アダプター

Hono は複数のランタイムで動作します。デフォルトのエクスポートは Cloudflare Workers、Deno、Bun で使用できます。Node.js では、Node アダプターを使用してください。

// Cloudflare Workers / Deno / Bun
export default app

// Node.js
import { serve } from '@hono/node-server'
serve(app)

ランタイム固有のヘルパー(serveStatic、upgradeWebSocket、getConnInfo、Lambda ハンドラー)は、Hono v5 以降、個別のパッケージに含まれています:@hono/cloudflare-workers、@hono/bun、@hono/deno、@hono/aws-lambda、@hono/lambda-edge、@hono/netlify、@hono/vercel、@hono/service-worker。使用するランタイムに対応したパッケージをインストールし、そこからインポートしてください。hono/<runtime> は v4 にのみ存在します。hono/adapter(env()、getRuntimeKey())は引き続き hono に含まれています。Hono v5 は ESM 専用であり、Node.js 22.12 以降が必要です。

コメント