This is the full developer documentation for Hono.
# Start of Hono documentation
# Hono
Hono - _**\[炎\] 🔥**_ - は小さく、シンプルで爆速なエッジ向け Web フレームワークです。
あらゆる JavaScript ランタイムで動作します: Cloudflare Workers 、 Fastly Compute 、 Deno 、 Bun 、 Vercel 、 Netlify 、 AWS Lambda 、 Lambda@Edge そして Node.js。
Hono は速いけど、速いだけではありません。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hono!'))
export default app
```
## クイックスタート
これを実行するだけです:
::: code-group
```sh [npm]
npm create hono@latest
```
```sh [yarn]
yarn create hono
```
```sh [pnpm]
pnpm create hono@latest
```
```sh [bun]
bun create hono@latest
```
```sh [deno]
deno init --npm hono@latest
```
:::
## 特徴
- **爆速** 🚀 - `RegExpRouter` は非常に高速なルーターです。 線形ループを使用しません。 めちゃくちゃ速い!
- **軽量** 🪶 - `hono/tiny` プリセットは 14KB 未満です。 Hono は依存関係が無く Web 標準のみを使用します。
- **マルチランタイム** 🌍 - Cloudflare Workers 、 Fastly Compute 、 Deno 、 Bun 、 AWS Lambda 、 Node.js で動作します。 同じコードがすべてのプラットフォーム上で動作します。
- **バッテリー同梱** 🔋 - Hono にはビルドインミドルウェア、カスタムミドルウェア、サードパーティーミドルウェア及びヘルパーが含まれています。 バッテリー同梱!
- **楽しい DX** 😃 - 非常にクリーンな API 。 最上級の TypeScript サポート。 Now, we've got "Types".
## 使用例
Hono は Express に似たフロントエンドを持たない Web アプリケーションフレームワークです。
しかし CDN エッジでミドルウェアを組み合わせることでより大規模なアプリケーションを構築できます。
以下にいくつかの使用例を紹介します。
- Web API の構築
- バックエンドサーバーのプロキシ
- CDN のフロント
- エッジアプリケーション
- ライブラリのベースサーバー
- フルスタックアプリケーション
## Hono を使用している主なプロジェクト
| プロジェクト | プラットフォーム | 内容 |
| ---------------------------------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| [cdnjs](https://cdnjs.com) | Cloudflare Workers | 無料かつオープンソースの CDN サービス。 _API サーバーに Hono を使用_。 |
| [Cloudflare D1](https://www.cloudflare.com/developer-platform/d1/) | Cloudflare Workers | サーバーレス SQL データベース。 _内部 API サーバーに Hono を使用_。 |
| [Cloudflare Workers KV](https://www.cloudflare.com/developer-platform/workers-kv/) | Cloudflare Workers | サーバーレス KVS (キーバリューストア)。 _内部 API サーバーに Hono を使用_。 |
| [BaseAI](https://baseai.dev) | Local AI Server | メモリ機能を持つサーバーレス AI エージェントパイプ。 Web 向けオープンソースエージェント AI フレームワーク。 _API サーバーに Hono を使用_。 |
| [Unkey](https://unkey.dev) | Cloudflare Workers | オープンソースの API 認証・認可プラットフォーム。 _API サーバーに Hono を使用_。 |
| [OpenStatus](https://openstatus.dev) | Bun | オープンソースの Web サイト・ API 監視プラットフォーム。 _API サーバーに Hono を使用_。 |
| [Deno Benchmarks](https://deno.com/benchmarks) | Deno | V8 上で動作する安全な TypeScript ランタイム。 _ベンチマーク用途に Hono を使用_。 |
| [Clerk](https://clerk.com) | Cloudflare Workers | オープンソースのユーザー管理プラットフォーム。 _API サーバーに Hono を使用_。 |
| [PostHog](https://posthog.com) | Node.js | セルフドライビングプロダクトを開発するためのオープンソースプラットフォーム。 _MCP サーバーに Hono を使用_。 |
そして、
- [Drivly](https://driv.ly/) - Cloudflare Workers
- [repeat.dev](https://repeat.dev/) - Cloudflare Workers
他のプロジェクトを見たいですか? [Who is using Hono in production?](https://github.com/orgs/honojs/discussions/1510) をご覧ください。
## Hono 1分クッキング
Hono を使用して Cloudflare Workers 向けのアプリケーションを作成するデモ。

## 爆速
**Hono は最速です**、 Cloudflare Workers 向けの他のルーターと比較してみましょう。
```
Hono x 402,820 ops/sec ±4.78% (80 runs sampled)
itty-router x 212,598 ops/sec ±3.11% (87 runs sampled)
sunder x 297,036 ops/sec ±4.76% (77 runs sampled)
worktop x 197,345 ops/sec ±2.40% (88 runs sampled)
Fastest is Hono
✨ Done in 28.06s.
```
[他のベンチマーク](/docs/concepts/benchmarks) も確認してください。
## 軽量
**Hono はとても小さいです**。 `hono/tiny` プリセットを使用した時、 Minify すれば **14KB 以下** になります。 ミドルウェアやアダプタはたくさんありますが、使用するときのみバンドルされます。 ちなみに Express は 572KB あります。
```
$ npx wrangler dev --minify ./src/index.ts
⛅️ wrangler 2.20.0
--------------------
⬣ Listening at http://0.0.0.0:8787
- http://127.0.0.1:8787
- http://192.168.128.165:8787
Total Upload: 11.47 KiB / gzip: 4.34 KiB
```
## 複数のルーター
**Hono は複数のルーターを持っています**。
**RegExpRouter** は JavaScript で最速のルーターです。 ディスパッチ前に作成された単一の巨大な正規表現を使用してルートを検索します。 **SmartRouter** と併用すると全てのルーティングパターンをサポートします。
**LinearRouter** はルートの登録が非常に高速なため、アプリケーションが毎回初期化される環境に適しています。 **PatternRouter** はパターンを追加して照合するだけなので小さくなります。
[ルーティングの詳細](/docs/concepts/routers)もご確認ください。
## Web 標準
**Web 標準**を使用しているおかげで、 Hono は沢山のプラットフォーム上で動作します。
- Cloudflare Workers
- Fastly Compute
- Deno
- Bun
- Vercel
- AWS Lambda
- Lambda@Edge
- その他...!
[Node.js アダプタ](https://github.com/honojs/node-server)を使って Hono は Node.js でも動きます。
[Web 標準についての詳細](/docs/concepts/web-standard)もご確認ください。
## ミドルウェア & ヘルパー
**Hono には沢山のミドルウェアやヘルパーがあります**。 それらは "Write Less, do more" を実現します。
Hono は以下のすぐに使えるミドルウェアとヘルパーを提供します:
- [Basic 認証](/docs/middleware/builtin/basic-auth)
- [Bearer 認証](/docs/middleware/builtin/bearer-auth)
- [Body Limit](/docs/middleware/builtin/body-limit)
- [キャッシュ](/docs/middleware/builtin/cache)
- [圧縮](/docs/middleware/builtin/compress)
- [Context Storage](/docs/middleware/builtin/context-storage)
- [Cookie](/docs/helpers/cookie)
- [CORS](/docs/middleware/builtin/cors)
- [ETag](/docs/middleware/builtin/etag)
- [html](/docs/helpers/html)
- [JSX](/docs/guides/jsx)
- [JWT 認証](/docs/middleware/builtin/jwt)
- [Logger](/docs/middleware/builtin/logger)
- [Language](/docs/middleware/builtin/language)
- [Pretty JSON](/docs/middleware/builtin/pretty-json)
- [Secure Headers](/docs/middleware/builtin/secure-headers)
- [SSG](/docs/helpers/ssg)
- [ストリーミング](/docs/helpers/streaming)
- [GraphQL サーバー](https://github.com/honojs/middleware/tree/main/packages/graphql-server)
- [Firebase 認証](https://github.com/honojs/middleware/tree/main/packages/firebase-auth)
- [Sentry](https://github.com/honojs/middleware/tree/main/packages/sentry)
- etc...!
例えば、 ETag と リクエストロギングを追加するためには Hono を使用して以下のコードを書くだけです:
```ts
import { Hono } from 'hono'
import { etag } from 'hono/etag'
import { logger } from 'hono/logger'
const app = new Hono()
app.use(etag(), logger())
```
[ミドルウェアの詳細](/docs/concepts/middleware)もご確認ください。
## 開発体験
Hono は楽しい "**開発体験**" を提供します。
`Context` オブジェクトによって Request/Response へ簡単にアクセスできます。
更に、 Hono は TypeScript で書かれており、 "**型**" を持っています。
例えば、パスパラメータはリテラル型になります。

そして、バリデーターと Hono Client `hc` は RPC モードを有効にします。 RPC モードでは、
Zod などのお気に入りのバリデーターを使用して、サーバーサイド API 仕様をクライアントと簡単に共有してタイプセーフなアプリケーションを構築できます。
[Hono Stacks](/docs/concepts/stacks)もご確認ください。
# サードパーティー製のミドルウェア
サードパーティーミドルウェアとは Hono のパッケージにバンドルされていないミドルウェアのことです。
ここにある殆どのミドルウェアは外部ライブラリを使用します。
### 認証
- [Auth.js(Next Auth)](https://github.com/honojs/middleware/tree/main/packages/auth-js)
- [Casbin](https://github.com/honojs/middleware/tree/main/packages/casbin)
- [Clerk Auth](https://github.com/honojs/middleware/tree/main/packages/clerk-auth)
- [Cloudflare Access](https://github.com/honojs/middleware/tree/main/packages/cloudflare-access)
- [OAuth Providers](https://github.com/honojs/middleware/tree/main/packages/oauth-providers)
- [OIDC Auth](https://github.com/honojs/middleware/tree/main/packages/oidc-auth)
- [Firebase Auth](https://github.com/honojs/middleware/tree/main/packages/firebase-auth)
- [Verify RSA JWT (JWKS)](https://github.com/wataruoguchi/verify-rsa-jwt-cloudflare-worker)
- [SSOJet Auth](https://github.com/ssojet/ssojet-hono)
- [Stytch Auth](https://github.com/honojs/middleware/tree/main/packages/stytch-auth)
- [Shopify Auth](https://github.com/besart-k/hono-shopify-auth)
### バリデーター
- [Ajv Validator](https://github.com/honojs/middleware/tree/main/packages/ajv-validator)
- [ArkType Validator](https://github.com/honojs/middleware/tree/main/packages/arktype-validator)
- [Class Validator](https://github.com/honojs/middleware/tree/main/packages/class-validator)
- [Conform Validator](https://github.com/honojs/middleware/tree/main/packages/conform-validator)
- [Effect Schema Validator](https://github.com/honojs/middleware/tree/main/packages/effect-validator)
- [Standard Schema Validator](https://github.com/honojs/middleware/tree/main/packages/standard-validator)
- [TypeBox Validator](https://github.com/honojs/middleware/tree/main/packages/typebox-validator)
- [Typia Validator](https://github.com/honojs/middleware/tree/main/packages/typia-validator)
- [unknownutil Validator](https://github.com/ryoppippi/hono-unknownutil-validator)
- [Valibot Validator](https://github.com/honojs/middleware/tree/main/packages/valibot-validator)
- [Zod Validator](https://github.com/honojs/middleware/tree/main/packages/zod-validator)
### OpenAPI
- [Zod OpenAPI](https://github.com/honojs/middleware/tree/main/packages/zod-openapi)
- [Scalar](https://github.com/scalar/scalar/tree/main/integrations/hono)
- [Swagger UI](https://github.com/honojs/middleware/tree/main/packages/swagger-ui)
- [Swagger Editor](https://github.com/honojs/middleware/tree/main/packages/swagger-editor)
- [Hono OpenAPI](https://github.com/rhinobase/hono-openapi)
- [hono-zod-openapi](https://github.com/paolostyle/hono-zod-openapi)
### Development
- [ESLint Config](https://github.com/honojs/middleware/tree/main/packages/eslint-config)
- [hono-testkit](https://github.com/md-sagban/hono-testkit)
- [SSG Plugin Essential](https://github.com/honojs/middleware/tree/main/packages/ssg-plugins-essential)
### Monitoring / Tracing
- [Apitally (API monitoring & analytics)](https://docs.apitally.io/frameworks/hono)
- [Highlight.io](https://www.highlight.io/docs/getting-started/backend-sdk/js/hono)
- [LogTape (Logging)](https://logtape.org/manual/integrations#hono)
- [OpenTelemetry](https://github.com/honojs/middleware/tree/main/packages/otel)
- [Prometheus Metrics](https://github.com/honojs/middleware/tree/main/packages/prometheus)
- [Sentry](https://github.com/honojs/middleware/tree/main/packages/sentry)
- [Pino logger](https://github.com/maou-shonen/hono-pino)
### Server / Adapter
- [GraphQL Server](https://github.com/honojs/middleware/tree/main/packages/graphql-server)
- [oRPC](https://orpc.dev/docs/adapters/hono)
- [tRPC Server](https://github.com/honojs/middleware/tree/main/packages/trpc-server)
- [mcp-use (MCP Server)](https://github.com/mcp-use/mcp-use)
- [hono-mcp-bridge (Expose routes as MCP tools)](https://github.com/serikshaikamalov/hono-mcp-bridge)
- [chanx-js (Typed WebSockets with AsyncAPI docs)](https://js.chanx.dev/server/integrations/hono)
### Transpiler
- [Bun Transpiler](https://github.com/honojs/middleware/tree/main/packages/bun-transpiler)
- [esbuild Transpiler](https://github.com/honojs/middleware/tree/main/packages/esbuild-transpiler)
### UI / Renderer
- [Qwik City](https://github.com/honojs/middleware/tree/main/packages/qwik-city)
- [React Compatibility](https://github.com/honojs/middleware/tree/main/packages/react-compat)
- [React Renderer](https://github.com/honojs/middleware/tree/main/packages/react-renderer)
### Queue / Job Processing
- [GlideMQ (Message Queue REST API + SSE)](https://github.com/avifenesh/glidemq-hono)
### Internationalization
- [Intlayer i18n](https://intlayer.org/doc/environment/hono)
### Utilities
- [Bun Compress](https://github.com/honojs/middleware/tree/main/packages/bun-compress)
- [Cap Checkpoint](https://capjs.js.org/guide/middleware/hono.html)
- [Event Emitter](https://github.com/honojs/middleware/tree/main/packages/event-emitter)
- [Geo](https://github.com/ktkongtong/hono-geo-middleware/tree/main/packages/middleware)
- [Guard Core (IP filtering, rate limiting, attack detection)](https://github.com/Guard-Core/guard-core-ts/tree/master/packages/hono)
- [Hono Rate Limiter](https://github.com/rhinobase/hono-rate-limiter)
- [Hono Problem Details (RFC 9457)](https://github.com/paveg/hono-problem-details)
- [Hono Simple DI](https://github.com/maou-shonen/hono-simple-DI)
- [InferDI](https://github.com/inferdi/inferdi/tree/main/packages/hono)
- [Idempotency (Stripe-style idempotency keys)](https://github.com/paveg/hono-idempotency)
- [idempot-js](https://js.idempot.dev) - spec-compliant middleware, supporting multiple storage backends (redis, postgres, mysql, sqlite)
- [jsonv-ts (Validator, OpenAPI, MCP)](https://github.com/dswbx/jsonv-ts)
- [MCP](https://github.com/honojs/middleware/tree/main/packages/mcp)
- [RONIN (Database)](https://github.com/ronin-co/hono-client)
- [Session](https://github.com/honojs/middleware/tree/main/packages/session)
- [StitchAPI (Typed, resilient API calls + SSE)](https://github.com/rejifald/StitchAPI/tree/main/packages/hono)
- [tsyringe](https://github.com/honojs/middleware/tree/main/packages/tsyringe)
- [User Agent based Blocker](https://github.com/honojs/middleware/tree/main/packages/ua-blocker)
# Basic 認証ミドルウェア
このミドルウェアは、特定のパスに Basic 認証を設定することができます。
Cloudflare Workers や他のプラットフォームで Basic 認証を実装することは思ったよりも複雑ですが、このミドルウェアを使えば簡単にできます。
Basic 認証方式が内部でどのように動くか知りたい場合は、 [MDN のドキュメント](https://developer.mozilla.org/ja/docs/Web/HTTP/Guides/Authentication#basic_%E8%AA%8D%E8%A8%BC%E6%96%B9%E5%BC%8F) をご覧ください。
## Import
```ts
import { Hono } from 'hono'
import { basicAuth } from 'hono/basic-auth'
```
## 使い方
```ts
const app = new Hono()
app.use(
'/auth/*',
basicAuth({
username: 'hono',
password: 'acoolproject',
})
)
app.get('/auth/page', (c) => {
return c.text('You are authorized')
})
```
特定のルート + メソッドを制限します:
```ts
const app = new Hono()
app.get('/auth/page', (c) => {
return c.text('Viewing page')
})
app.delete(
'/auth/page',
basicAuth({ username: 'hono', password: 'acoolproject' }),
(c) => {
return c.text('Page deleted')
}
)
```
ユーザーを自分で検証したい場合は、 `verifyUser` オプションを指定してください。 `true` を返すことで、資格情報が受け入れられたことを示します。
```ts
const app = new Hono()
app.use(
basicAuth({
verifyUser: (username, password, c) => {
return (
username === 'dynamic-user' && password === 'hono-password'
)
},
})
)
```
## オプション
### username: `string`
認証されている人のユーザー名。
### password: `string`
そのユーザーのパスワード。
### realm: `string`
返される WWW-Authenticate チャレンジヘッダーの一部としてのレルムのドメイン名です。 デフォルトは `"Secure Area"` です。
詳しくは: https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Headers/WWW-Authenticate#digest
### hashFunction: `Function`
パスワードを安全に比較するためのハッシュ関数。
### verifyUser: `(username: string, password: string, c: Context) => boolean | Promise`
ユーザーを検証する関数。
### invalidUserMessage: `string | object | MessageFunction`
`MessageFunction` は `(c: Context) => string | object | Promise` 。 ユーザーが無効だったときのメッセージ。
### onAuthSuccess: `(c: Context, username: string) => void | Promise`
認証が成功した後に呼び出されるコールバック関数です。 これにより、 Authorization ヘッダーを再パースすることなく、コンテキスト変数を設定したり副作用を実行したりできます。
```ts
app.use(
'/auth/*',
basicAuth({
username: 'hono',
password: 'acoolproject',
onAuthSuccess: (c, username) => {
c.set('username', username)
},
})
)
app.get('/auth/page', (c) => {
const username = c.get('username')
return c.text(`Hello, ${username}!`)
})
```
## More Options
### ...users: `{ username: string, password: string }[]`
## レシピ
### 複数ユーザーの設定
このミドルウェアでは、 `username` と `password` のペアを含む任意の数のオブジェクトをパラメータとして渡すこともできます。
```ts
app.use(
'/auth/*',
basicAuth(
{
username: 'hono',
password: 'acoolproject',
// Define other params in the first object
realm: 'www.example.com',
},
{
username: 'hono-admin',
password: 'super-secure',
// Cannot redefine other params here
},
{
username: 'hono-user-1',
password: 'a-secret',
// Or here
}
)
)
```
Or less hardcoded:
```ts
import { users } from '../config/users'
app.use(
'/auth/*',
basicAuth(
{
realm: 'www.example.com',
...users[0],
},
...users.slice(1)
)
)
```
# Bearer Auth ミドルウェア
Bearer Auth Middleware は、リクエストヘッダー内の API トークンを検証することで認証を提供します。
エンドポイントにアクセスする HTTP クライアントは、ヘッダー値として `Bearer {token}` を付けた `Authorization` ヘッダーを追加します。
ターミナルから `curl` を使用すると、次のようになります:
```sh
curl -H 'Authorization: Bearer honoiscool' http://localhost:8787/auth/page
```
## Import
```ts
import { Hono } from 'hono'
import { bearerAuth } from 'hono/bearer-auth'
```
## 使い方
> [!NOTE]
> `token` は正規表現 `/[A-Za-z0-9._~+/-]+=*/` にマッチする必要があります。 そうでない場合は 400 エラーが返されます。 特に、この正規表現は URL セーフ Base64 と標準 Base64 でエンコードされた JWT の両方に対応しています。 このミドルウェアは、ベアラートークンが JWT であることを要求せず、上記の正規表現にマッチすることのみを要求します。
```ts
const app = new Hono()
const token = 'honoiscool'
app.use('/api/*', bearerAuth({ token }))
app.get('/api/page', (c) => {
return c.json({ message: 'You are authorized' })
})
```
特定のルート + メソッドに制限する場合:
```ts
const app = new Hono()
const token = 'honoiscool'
app.get('/api/page', (c) => {
return c.json({ message: 'Read posts' })
})
app.post('/api/page', bearerAuth({ token }), (c) => {
return c.json({ message: 'Created post!' }, 201)
})
```
複数のトークンを実装する場合 (例えば、任意の有効なトークンは読み取り可能だが、作成/更新/削除は特権トークンに制限される):
```ts
const app = new Hono()
const readToken = 'read'
const privilegedToken = 'read+write'
const privilegedMethods = ['POST', 'PUT', 'PATCH', 'DELETE']
app.on('GET', '/api/page/*', async (c, next) => {
// List of valid tokens
const bearer = bearerAuth({ token: [readToken, privilegedToken] })
return bearer(c, next)
})
app.on(privilegedMethods, '/api/page/*', async (c, next) => {
// Single valid privileged token
const bearer = bearerAuth({ token: privilegedToken })
return bearer(c, next)
})
// Define handlers for GET, POST, etc.
```
トークンの値を自分で検証したい場合は、 `verifyToken` オプションを指定してください。 `true` を返すと受け入れられたことを意味します。
```ts
const app = new Hono()
app.use(
'/auth-verify-token/*',
bearerAuth({
verifyToken: async (token, c) => {
return token === 'dynamic-token'
},
})
)
```
## オプション
### token: `string` | `string[]`
受信したベアラートークンを検証するための文字列です。
### realm: `string`
返される WWW-Authenticate チャレンジヘッダーの一部としてのレルムのドメイン名です。 デフォルトは `""` です。
詳しくは次を参照してください: https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/WWW-Authenticate#directives
### prefix: `string`
Authorization ヘッダー値のプレフィックス (`schema` とも呼ばれます) です。 デフォルトは `"Bearer"` です。
### headerName: `string`
ヘッダー名です。 デフォルト値は `Authorization` です。
### hashFunction: `Function`
認証トークンを安全に比較するためのハッシュ処理を行う関数です。
### verifyToken: `(token: string, c: Context) => boolean | Promise`
トークンを検証する関数です。
### noAuthenticationHeader: `object`
リクエストに認証ヘッダーがない場合のエラーレスポンスをカスタマイズします。
- `wwwAuthenticateHeader`: `string | object | MessageFunction` - WWW-Authenticate ヘッダー値をカスタマイズします。
- `message`: `string | object | MessageFunction` - レスポンスボディのカスタムメッセージです。
`MessageFunction` は `(c: Context) => string | object | Promise` です。
### invalidAuthenticationHeader: `object`
認証ヘッダーの形式が無効な場合のエラーレスポンスをカスタマイズします。
- `wwwAuthenticateHeader`: `string | object | MessageFunction` - WWW-Authenticate ヘッダー値をカスタマイズします。
- `message`: `string | object | MessageFunction` - レスポンスボディのカスタムメッセージです。
### invalidToken: `object`
トークンが無効な場合のエラーレスポンスをカスタマイズします。
- `wwwAuthenticateHeader`: `string | object | MessageFunction` - WWW-Authenticate ヘッダー値をカスタマイズします。
- `message`: `string | object | MessageFunction` - レスポンスボディのカスタムメッセージです。
# Body Limit ミドルウェア
Body Limit ミドルウェアは、リクエストボディのファイルサイズを制限できます。
このミドルウェアは、まずリクエスト内の `Content-Length` ヘッダーの値を使用します (存在する場合)。
設定されていない場合は、ボディをストリームとして読み込み、指定されたファイルサイズより大きい場合にエラーハンドラーを実行します。
## Import
```ts
import { Hono } from 'hono'
import { bodyLimit } from 'hono/body-limit'
```
## 使い方
```ts
const app = new Hono()
app.post(
'/upload',
bodyLimit({
maxSize: 50 * 1024, // 50kb
onError: (c) => {
return c.text('overflow :(', 413)
},
}),
async (c) => {
const body = await c.req.parseBody()
if (body['file'] instanceof File) {
console.log(`Got file sized: ${body['file'].size}`)
}
return c.text('pass :)')
}
)
```
## オプション
### maxSize: `number`
制限したいファイルの最大ファイルサイズです。 デフォルトは `100 * 1024` - `100kb` です。
### onError: `OnError`
指定されたファイルサイズを超えた場合に呼び出されるエラーハンドラーです。
## Bun での大きなリクエストの扱い
Body Limit ミドルウェアを、デフォルトよりも大きいリクエストボディを許可するために明示的に使用する場合、それに応じて `Bun.serve` の設定を変更する必要があるかもしれません。 [執筆時点](https://github.com/oven-sh/bun/blob/f2cfa15e4ef9d730fc6842ad8b79fb7ab4c71cb9/packages/bun-types/bun.d.ts#L2191) では、 `Bun.serve` のデフォルトのリクエストボディ上限は 128MiB です。 Hono の Body Limit ミドルウェアにそれより大きい値を設定しても、リクエストは失敗し続け、さらにミドルウェアで指定された `onError` ハンドラーも呼び出されません。 これは、 `Bun.serve()` がステータスコードを `413` に設定し、リクエストを Hono に渡す前に接続を切断するためです。
Hono と Bun で 128MiB より大きいリクエストを受け付けたい場合は、 Bun 側にも上限を設定する必要があります:
```ts
export default {
port: process.env['PORT'] || 3000,
fetch: app.fetch,
maxRequestBodySize: 1024 * 1024 * 200, // your value here
}
```
または、セットアップによっては次のようになります:
```ts
Bun.serve({
fetch(req, server) {
return app.fetch(req, { ip: server.requestIP(req) })
},
maxRequestBodySize: 1024 * 1024 * 200, // your value here
})
```
# Cache ミドルウェア
Cache ミドルウェアは、 Web 標準の [Cache API](https://developer.mozilla.org/ja/docs/Web/API/Cache) を使用します。
Cache ミドルウェアは現在、カスタムドメインを使用する Cloudflare Workers プロジェクトと、 [Deno 1.26+](https://github.com/denoland/deno/releases/tag/v1.26.0) を使用する Deno プロジェクトをサポートしています。 Deno Deploy でも利用できます。
Cloudflare Workers は `Cache-Control` ヘッダーを尊重し、キャッシュされたレスポンスを返します。 詳細については、 [Cloudflare Docs の Cache](https://developers.cloudflare.com/workers/runtime-apis/cache/) を参照してください。 Deno はヘッダーを尊重しないため、キャッシュを更新する必要がある場合は、独自のメカニズムを実装する必要があります。
各プラットフォームでの手順については、後述の [使い方](#usage) を参照してください。
## Import
```ts
import { Hono } from 'hono'
import { cache } from 'hono/cache'
```
## 使い方
::: code-group
```ts [Cloudflare Workers]
app.get(
'*',
cache({
cacheName: 'my-app',
cacheControl: 'max-age=3600',
})
)
```
```ts [Deno]
// Must use `wait: true` for the Deno runtime
app.get(
'*',
cache({
cacheName: 'my-app',
cacheControl: 'max-age=3600',
wait: true,
})
)
```
:::
## 認証済みレスポンスのキャッシュ
キャッシュはすべてのクライアントで共有され、サインインしたユーザー向けのレスポンスを含む、キャッシュ可能なすべてのレスポンスを保存します。 ミドルウェアはリクエストの `Cookie` ヘッダーを参照しません。 ユーザーごとのレスポンスを共有キャッシュの外に保つには:
- 認証ミドルウェアの後に `cache()` を登録し、未認証のリクエストがキャッシュに到達しないようにします。
- ユーザーごとのレスポンスに `Cache-Control: private` (または `no-store`) を設定します。 ミドルウェアはそれらを保存しません。
- または `vary` オプションを使用します。 例えば `vary: 'Cookie'` とすると、Cookie の値ごとに個別のレスポンスをキャッシュします。
```ts
app.use('/api/*', auth)
app.get(
'/api/*',
cache({
cacheName: 'my-app',
cacheControl: 'max-age=60',
vary: 'Cookie',
})
)
```
## QUERY リクエストのキャッシュ
Cache ミドルウェアは、 [QUERY](https://www.rfc-editor.org/rfc/rfc10008.html) リクエストへのレスポンスもキャッシュします。 RFC 10008 の要件に従い、 QUERY リクエストのキャッシュキーにはリクエストコンテンツとその表現メタデータのダイジェストが組み込まれるため、ボディが異なるリクエストは個別にキャッシュされます。
`maxQueryBodySize` (デフォルトは 64 KiB) より大きいボディを持つ QUERY リクエストは、キャッシュをバイパスします。
::: info
これをサポートするために、キャッシュされたエントリは、リクエスト URL 自体の代わりに `/.hono/cache?__hono_cache_key=...` という形式の内部キーで保存されます。 Cache API を通じて直接キャッシュエントリをパージする場合 (例えば、元のリクエスト URL で `caches.delete()` を呼び出す場合) 、そのロジックを更新する必要があります。 これは GET を含むすべてのメソッドに適用されます。
:::
## オプション
### cacheName: `string` | `(c: Context) => string` | `Promise`
キャッシュの名前です。 異なる識別子を持つ複数のキャッシュを保存するために使用できます。
### wait: `boolean`
Hono がリクエストの処理を続行する前に、 `cache.put` 関数の Promise の解決を待つべきかどうかを示すブール値です。 _Deno 環境では true である必要があります_ 。 デフォルトは `false` です。
### cacheControl: `string`
`Cache-Control` ヘッダーのためのディレクティブ文字列です。 詳しくは [MDN ドキュメント](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cache-Control) を参照してください。 このオプションが指定されていない場合、リクエストに `Cache-Control` ヘッダーは追加されません。
### vary: `string` | `string[]`
レスポンスに `Vary` ヘッダーを設定します。 元のレスポンスヘッダーに既に `Vary` ヘッダーが含まれている場合、重複を削除した上で値がマージされます。 これを `*` に設定するとエラーになります。 Vary ヘッダーとキャッシング戦略への影響について詳しくは、 [MDN ドキュメント](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Vary) を参照してください。
### keyGenerator: `(c: Context) => string | Promise`
`cacheName` ストア内のすべてのリクエストに対してキーを生成します。 これを使用して、リクエストパラメータやコンテキストパラメータに基づいてデータをキャッシュできます。 デフォルトは `c.req.url` です。 QUERY リクエストの場合、キーにはさらにリクエストコンテンツとその表現メタデータのダイジェストが含まれます。
### maxQueryBodySize: `number`
キャッシュ可能な QUERY リクエストのボディサイズの最大値 (バイト) です。 それより大きいボディを持つ QUERY リクエストはキャッシュをバイパスします。 デフォルトは `65536` (64 KiB) です。
### cacheableStatusCodes: `number[]`
キャッシュすべきステータスコードの配列です。 デフォルトは `[200]` です。 このオプションを使用して、特定のステータスコードを持つレスポンスをキャッシュします。
```ts
app.get(
'*',
cache({
cacheName: 'my-app',
cacheControl: 'max-age=3600',
cacheableStatusCodes: [200, 404, 412],
})
)
```
### onCacheNotAvailable: `((reason: string) => void | Promise)` | `false`
Cache API がグローバルスコープで利用できない場合や、 QUERY キャッシングが Web Crypto を使用できない場合の挙動を制御するコールバック関数または `false` です。 コールバックは理由とともに呼び出されます。 デフォルトでは、理由は `console.log` でログに出力されます。 カスタム関数を提供して挙動をカスタマイズしたり、 `false` を設定してログを完全に抑制したりできます。
```ts
// Custom logging
app.use(
cache({
cacheName: 'my-app-v1',
onCacheNotAvailable: () => {
console.log('Custom log: Cache API is not available.')
},
})
)
```
```ts
// Suppress logging
app.use(
cache({
cacheName: 'my-app-v1',
onCacheNotAvailable: false,
})
)
```
# Combine ミドルウェア
Combine Middleware は、複数のミドルウェア関数を1つのミドルウェアに結合します。 次の3つの関数を提供します:
- `some` - 与えられたミドルウェアのうち1つだけを実行します。
- `every` - 与えられたすべてのミドルウェアを実行します。
- `except` - 条件が満たされない場合にのみ、与えられたすべてのミドルウェアを実行します。
## Import
```ts
import { Hono } from 'hono'
import { some, every, except } from 'hono/combine'
```
## 使い方
Combine Middleware を使った複雑なアクセス制御ルールの例です。
```ts
import { Hono } from 'hono'
import { bearerAuth } from 'hono/bearer-auth'
import { getConnInfo } from '@hono/cloudflare-workers'
import { every, some } from 'hono/combine'
import { ipRestriction } from 'hono/ip-restriction'
import { rateLimit } from '@/my-rate-limit'
const app = new Hono()
app.use(
'*',
some(
every(
ipRestriction(getConnInfo, { allowList: ['192.168.0.2'] }),
bearerAuth({ token })
),
// If both conditions are met, rateLimit will not execute.
rateLimit()
)
)
app.get('/', (c) => c.text('Hello Hono!'))
```
### some
true を返した最初のミドルウェアを実行します。 ミドルウェアは順番に適用され、いずれかのミドルウェアが正常に終了すると、それ以降のミドルウェアは実行されません。
```ts
import { some } from 'hono/combine'
import { bearerAuth } from 'hono/bearer-auth'
import { myRateLimit } from '@/rate-limit'
// If client has a valid token, skip rate limiting.
// Otherwise, apply rate limiting.
app.use(
'/api/*',
some(bearerAuth({ token }), myRateLimit({ limit: 100 }))
)
```
### every
すべてのミドルウェアを実行し、いずれかが失敗した場合は停止します。 ミドルウェアは順番に適用され、いずれかのミドルウェアがエラーをスローすると、それ以降のミドルウェアは実行されません。
```ts
import { some, every } from 'hono/combine'
import { bearerAuth } from 'hono/bearer-auth'
import { myCheckLocalNetwork } from '@/check-local-network'
import { myRateLimit } from '@/rate-limit'
// If client is in local network, skip authentication and rate limiting.
// Otherwise, apply authentication and rate limiting.
app.use(
'/api/*',
some(
myCheckLocalNetwork(),
every(bearerAuth({ token }), myRateLimit({ limit: 100 }))
)
)
```
### except
条件が満たされていない場合にすべてのミドルウェアを実行します。 条件として文字列または関数を渡せます。 複数のターゲットにマッチさせる必要がある場合は、配列として渡します。
```ts
import { except } from 'hono/combine'
import { bearerAuth } from 'hono/bearer-auth'
// If client is accessing public API, skip authentication.
// Otherwise, require a valid token.
app.use('/api/*', except('/api/public/*', bearerAuth({ token })))
```
# Compress ミドルウェア
このミドルウェアは、 `Accept-Encoding` リクエストヘッダーに応じてレスポンスボディを圧縮します。
::: info
**Note**: Cloudflare Workers と Deno Deploy では、レスポンスボディは自動的に圧縮されるため、このミドルウェアを使用する必要はありません。
:::
## Import
```ts
import { Hono } from 'hono'
import { compress } from 'hono/compress'
```
## 使い方
```ts
const app = new Hono()
app.use(compress())
```
## オプション
### encoding: `'gzip'` | `'deflate'`
レスポンスの圧縮に許可する圧縮方式です。 `gzip` または `deflate` のいずれかです。 定義しない場合は両方が許可され、 `Accept-Encoding` ヘッダーに基づいて使用されます。 このオプションが指定されておらず、クライアントが `Accept-Encoding` ヘッダーで両方を指定している場合は、 `gzip` が優先されます。
### threshold: `number`
圧縮する最小サイズ (バイト) です。 デフォルトは 1024 バイトです。
### contentTypeFilter: `RegExp` | `(contentType: string) => boolean`
`Content-Type` に基づいてレスポンスを圧縮するかどうかを判定するための `RegExp` または関数です。 デフォルトでは、組み込みの圧縮可能な Content-Type のリストが使用されます。
マッチする Content-Types のみを圧縮したい場合は、 `RegExp` を渡せます:
```ts
// Compress only JSON responses
app.use(compress({ contentTypeFilter: /^application\/json/ }))
```
または、カスタムロジックのために関数を渡せます。 組み込みの `COMPRESSIBLE_CONTENT_TYPE_REGEX` もエクスポートされているので、デフォルトの挙動を拡張できます:
```ts
import {
compress,
COMPRESSIBLE_CONTENT_TYPE_REGEX,
} from 'hono/compress'
// Compress the default Content-Types plus a custom one
app.use(
compress({
contentTypeFilter: (type) =>
COMPRESSIBLE_CONTENT_TYPE_REGEX.test(type) ||
type === 'application/x-myformat',
})
)
```
# Context Storage ミドルウェア
Context Storage Middleware は、 Hono の `Context` を `AsyncLocalStorage` に保存し、グローバルにアクセスできるようにします。
::: info
**Note** このミドルウェアは `AsyncLocalStorage` を使用します。 ランタイムがこれをサポートしている必要があります。
**Cloudflare Workers**: `AsyncLocalStorage` を有効にするには、 `wrangler.toml` ファイルに [`nodejs_compat` または `nodejs_als` フラグ](https://developers.cloudflare.com/workers/configuration/compatibility-dates/#nodejs-compatibility-flag) を追加してください。
:::
## Import
```ts
import { Hono } from 'hono'
import {
contextStorage,
getContext,
tryGetContext,
} from 'hono/context-storage'
```
## 使い方
`contextStorage()` がミドルウェアとして適用されている場合、 `getContext()` は現在の Context オブジェクトを返します。
```ts
type Env = {
Variables: {
message: string
}
}
const app = new Hono()
app.use(contextStorage())
app.use(async (c, next) => {
c.set('message', 'Hello!')
await next()
})
// You can access the variable outside the handler.
const getMessage = () => {
return getContext().var.message
}
app.get('/', (c) => {
return c.text(getMessage())
})
```
Cloudflare Workers では、ハンドラーの外部から Bindings にアクセスできます。
```ts
type Env = {
Bindings: {
KV: KVNamespace
}
}
const app = new Hono()
app.use(contextStorage())
const setKV = (value: string) => {
return getContext().env.KV.put('key', value)
}
```
## tryGetContext
`tryGetContext()` は `getContext()` と同様に動作しますが、コンテキストが利用できない場合にエラーをスローする代わりに `undefined` を返します:
```ts
const context = tryGetContext()
if (context) {
// Context is available
console.log(context.var.message)
}
```
# CORS ミドルウェア
Web API としての Cloudflare Workers のユースケースは多く、外部のフロントエンドアプリケーションから呼び出されます。
そのためには CORS を実装する必要があります。 これもミドルウェアで実装しましょう。
## Import
```ts
import { Hono } from 'hono'
import { cors } from 'hono/cors'
```
## 使い方
```ts
const app = new Hono()
// CORS should be called before the route
app.use('/api/*', cors())
app.use(
'/api2/*',
cors({
origin: 'http://example.com',
allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'],
allowMethods: ['POST', 'GET', 'OPTIONS'],
exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],
maxAge: 600,
credentials: true,
})
)
app.all('/api/abc', (c) => {
return c.json({ success: true })
})
app.all('/api2/abc', (c) => {
return c.json({ success: true })
})
```
複数のオリジン:
```ts
app.use(
'/api3/*',
cors({
origin: ['https://example.com', 'https://example.org'],
})
)
// Or you can use "function"
app.use(
'/api4/*',
cors({
// `c` is a `Context` object
origin: (origin, c) => {
return origin.endsWith('.example.com')
? origin
: 'http://example.com'
},
})
)
```
オリジンに基づく動的な許可メソッド:
```ts
app.use(
'/api5/*',
cors({
origin: (origin) =>
origin === 'https://example.com' ? origin : '*',
// `c` is a `Context` object
allowMethods: (origin, c) =>
origin === 'https://example.com'
? ['GET', 'HEAD', 'POST', 'PATCH', 'DELETE']
: ['GET', 'HEAD'],
})
)
```
## オプション
### origin: `string` | `string[]` | `(origin:string, c:Context) => string`
"_Access-Control-Allow-Origin_" CORS ヘッダーの値です。 `origin: (origin) => (origin.endsWith('.example.com') ? origin : 'http://example.com')` のようなコールバック関数を渡すこともできます。 デフォルトは `*` です。
### allowMethods: `string[]` | `(origin:string, c:Context) => string[]`
"_Access-Control-Allow-Methods_" CORS ヘッダーの値です。 オリジンに基づいて許可メソッドを動的に決定するコールバック関数を渡すこともできます。 デフォルトは `['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH', 'QUERY']` です。
### allowHeaders: `string[]`
"_Access-Control-Allow-Headers_" CORS ヘッダーの値です。 デフォルトは `[]` です。
### maxAge: `number`
"_Access-Control-Max-Age_" CORS ヘッダーの値です。
### credentials: `boolean`
"_Access-Control-Allow-Credentials_" CORS ヘッダーの値です。
### exposeHeaders: `string[]`
"_Access-Control-Expose-Headers_" CORS ヘッダーの値です。 デフォルトは `[]` です。
## 環境に依存する CORS の設定
開発環境や本番環境など、実行環境に応じて CORS の設定を調整したい場合は、環境変数から値を注入すると便利です。 これにより、アプリケーションが自身の実行環境を意識する必要がなくなります。 以下の例を参考にしてください。
```ts
app.use('*', async (c, next) => {
const corsMiddlewareHandler = cors({
origin: c.env.CORS_ORIGIN,
})
return corsMiddlewareHandler(c, next)
})
```
## Vite との併用
Hono を Vite と併用する場合は、 `vite.config.ts` で `server.cors` を `false` に設定して、 Vite の組み込み CORS 機能を無効にする必要があります。 これは、 Hono の CORS ミドルウェアとの競合を防ぎます。
```ts
// vite.config.ts
import { cloudflare } from '@cloudflare/vite-plugin'
import { defineConfig } from 'vite'
export default defineConfig({
server: {
cors: false, // disable Vite's built-in CORS setting
},
plugins: [cloudflare()],
})
```
# CSRF 保護
このミドルウェアは、 `Origin` ヘッダーと `Sec-Fetch-Site` ヘッダーの両方をチェックすることで、 CSRF 攻撃から保護します。 いずれかの検証に合格すれば、リクエストは許可されます。
ミドルウェアが検証するのは、次のようなリクエストのみです:
- 安全でない HTTP メソッドを使用している (GET 、 HEAD 、 OPTIONS 以外)
- HTML フォームで送信可能なコンテンツタイプを持っている (`application/x-www-form-urlencoded` 、 `multipart/form-data` 、 `text/plain`)
`Origin` ヘッダーを送信しない古いブラウザや、リバースプロキシでこれらのヘッダーを削除する環境では、うまく動作しない場合があります。 そのような環境では、他の CSRF トークン方式を使用してください。
## Import
```ts
import { Hono } from 'hono'
import { csrf } from 'hono/csrf'
```
## 使い方
```ts
const app = new Hono()
// Default: both origin and sec-fetch-site validation
app.use(csrf())
// Allow specific origins
app.use(csrf({ origin: 'https://myapp.example.com' }))
// Allow multiple origins
app.use(
csrf({
origin: [
'https://myapp.example.com',
'https://development.myapp.example.com',
],
})
)
// Allow specific sec-fetch-site values
app.use(csrf({ secFetchSite: 'same-origin' }))
app.use(csrf({ secFetchSite: ['same-origin', 'none'] }))
// Dynamic origin validation
// It is strongly recommended that the protocol be verified to ensure a match to `$`.
// You should *never* do a forward match.
app.use(
'*',
csrf({
origin: (origin) =>
/https:\/\/(\w+\.)?myapp\.example\.com$/.test(origin),
})
)
// Dynamic sec-fetch-site validation
app.use(
csrf({
secFetchSite: (secFetchSite, c) => {
// Always allow same-origin
if (secFetchSite === 'same-origin') return true
// Allow cross-site for webhook endpoints
if (
secFetchSite === 'cross-site' &&
c.req.path.startsWith('/webhook/')
) {
return true
}
return false
},
})
)
```
## オプション
### origin: `string` | `string[]` | `Function`
CSRF 保護で許可するオリジンを指定します。
- **`string`**: 単一の許可オリジン (例: `'https://example.com'`)
- **`string[]`**: 許可オリジンの配列
- **`Function`**: 柔軟なオリジン検証とバイパスロジックのためのカスタムハンドラ `(origin: string, context: Context) => boolean`
**Default**: リクエスト URL と同じオリジンのみ
関数ハンドラは、リクエストの `Origin` ヘッダーの値とリクエストコンテキストを受け取ります。 これにより、パス、ヘッダー、その他のコンテキストデータなどのリクエストプロパティに基づいた動的な検証が可能になります。
### secFetchSite: `string` | `string[]` | `Function`
[Fetch Metadata](https://web.dev/articles/fetch-metadata) を使用した CSRF 保護で許可する Sec-Fetch-Site ヘッダーの値を指定します。
- **`string`**: 単一の許可値 (例: `'same-origin'`)
- **`string[]`**: 許可値の配列 (例: `['same-origin', 'none']`)
- **`Function`**: 柔軟な検証のためのカスタムハンドラ `(secFetchSite: string, context: Context) => boolean`
**Default**: `'same-origin'` のみ許可
標準の Sec-Fetch-Site の値:
- `same-origin`: 同一オリジンからのリクエスト
- `same-site`: 同一サイト (異なるサブドメイン) からのリクエスト
- `cross-site`: 異なるサイトからのリクエスト
- `none`: Web ページ以外からのリクエスト (例: ブラウザのアドレスバー、ブックマーク)
関数ハンドラは、リクエストの `Sec-Fetch-Site` ヘッダーの値とリクエストコンテキストを受け取り、リクエストのプロパティに基づいた動的な検証を可能にします。
# ETag ミドルウェア
このミドルウェアを使用すると、 ETag ヘッダーを簡単に追加できます。
## Import
```ts
import { Hono } from 'hono'
import { etag } from 'hono/etag'
```
## 使い方
```ts
const app = new Hono()
app.use('/etag/*', etag())
app.get('/etag/abc', (c) => {
return c.text('Hono is cool')
})
```
## 保持されるヘッダー
304 レスポンスには、同等の 200 OK レスポンスで送信されるはずだったヘッダーを含める必要があります。 デフォルトのヘッダーは、 Cache-Control 、 Content-Location 、 Date 、 ETag 、 Expires 、 Vary です。
送信されるヘッダーを追加したい場合は、 `retainedHeaders` オプションと、デフォルトのヘッダーを含む `RETAINED_304_HEADERS` 文字列配列変数を使用できます:
```ts
import { etag, RETAINED_304_HEADERS } from 'hono/etag'
// ...
app.use(
'/etag/*',
etag({
retainedHeaders: ['x-message', ...RETAINED_304_HEADERS],
})
)
```
## オプション
### weak: `boolean`
[弱い検証](https://developer.mozilla.org/ja/docs/Web/HTTP/Conditional_requests#weak_validation) を使用するかどうかを定義します。 `true` が設定されると、値のプレフィックスに `w/` が追加されます。 デフォルトは `false` です。
### retainedHeaders: `string[]`
304 レスポンスに保持したいヘッダーです。
### generateDigest: `(body: Uint8Array) => ArrayBuffer | Promise`
カスタムダイジェスト生成関数です。 デフォルトでは `SHA-1` を使用します。 この関数はレスポンスボディを `Uint8Array` として受け取り、ハッシュを `ArrayBuffer` またはその Promise として返す必要があります。
# IP Restriction ミドルウェア
IP Restriction Middleware は、ユーザーの IP アドレスに基づいてリソースへのアクセスを制限するミドルウェアです。
## Import
```ts
import { Hono } from 'hono'
import { ipRestriction } from 'hono/ip-restriction'
```
## 使い方
Bun 上で動作するアプリケーションで、ローカルからのアクセスのみを許可したい場合は、次のように書けます。 拒否したいルールを `denyList` に、許可したいルールを `allowList` に指定します。
```ts
import { Hono } from 'hono'
import { getConnInfo } from '@hono/bun'
import { ipRestriction } from 'hono/ip-restriction'
const app = new Hono()
app.use(
'*',
ipRestriction(getConnInfo, {
denyList: [],
allowList: ['127.0.0.1', '::1'],
})
)
app.get('/', (c) => c.text('Hello Hono!'))
```
環境に適した [ConnInfo Helper](/docs/helpers/conninfo) の `getConninfo` を `ipRestriction` の第1引数として渡します。 例えば、 Deno の場合は次のようになります:
```ts
import { getConnInfo } from '@hono/deno'
import { ipRestriction } from 'hono/ip-restriction'
//...
app.use(
'*',
ipRestriction(getConnInfo, {
// ...
})
)
```
## ルール
ルールの書き方は以下の手順に従ってください。
### IPv4
- `192.168.2.0` - Static IP Address
- `192.168.2.0/24` - CIDR Notation
- `*` - ALL Addresses
### IPv6
- `::1` - Static IP Address
- `::1/10` - CIDR Notation
- `*` - ALL Addresses
## エラー処理
エラーをカスタマイズするには、第3引数で `Response` を返します。
```ts
app.use(
'*',
ipRestriction(
getConnInfo,
{
denyList: ['192.168.2.0/24'],
},
async (remote, c) => {
return c.text(`Blocking access from ${remote.addr}`, 403)
}
)
)
```
# JSX Renderer ミドルウェア
JSX Renderer Middleware は、 `c.setRenderer()` を使うことなく、 `c.render()` 関数で JSX をレンダリングする際のレイアウトを設定できるようにします。 さらに、 `useRequestContext()` を使用して、コンポーネント内の Context のインスタンスにアクセスできるようにします。
## Import
```ts
import { Hono } from 'hono'
import { jsxRenderer, useRequestContext } from 'hono/jsx-renderer'
```
## 使い方
```jsx
const app = new Hono()
app.get(
'/page/*',
jsxRenderer(({ children }) => {
return (
{children}
)
})
)
app.get('/page/about', (c) => {
return c.render(About me!
)
})
```
## オプション
### docType: `boolean` | `string`
HTML の先頭に DOCTYPE を追加したくない場合は、 `docType` オプションを `false` に設定してください。
```tsx
app.use(
'*',
jsxRenderer(
({ children }) => {
return (
{children}
)
},
{ docType: false }
)
)
```
また、 DOCTYPE を指定することもできます。
```tsx
app.use(
'*',
jsxRenderer(
({ children }) => {
return (
{children}
)
},
{
docType:
'',
}
)
)
```
### stream: `boolean` | `Record`
`true` を設定するか Record 値を指定すると、ストリーミングレスポンスとしてレンダリングされます。
```tsx
const AsyncComponent = async () => {
await new Promise((r) => setTimeout(r, 1000)) // sleep 1s
return Hi!
}
app.get(
'*',
jsxRenderer(
({ children }) => {
return (
SSR Streaming
{children}
)
},
{ stream: true }
)
)
app.get('/', (c) => {
return c.render(
loading...}>
)
})
```
`true` を設定すると、次のヘッダーが追加されます:
```ts
{
'Transfer-Encoding': 'chunked',
'Content-Type': 'text/html; charset=UTF-8',
'Content-Encoding': 'Identity'
}
```
Record 値を指定することで、ヘッダー値をカスタマイズできます。
### Function-based Options
静的なオプションオブジェクトの代わりに、 `Context` オブジェクトを受け取る関数を渡せます。 これにより、環境変数やリクエストパラメータなど、リクエストコンテキストに基づいてオプションを動的に設定できます。
```tsx
app.use(
'*',
jsxRenderer(
({ children }) => {
return (
{children}
)
},
(c) => ({
stream: c.req.header('X-Enable-Streaming') === 'true',
})
)
)
```
具体的な例として、 [`isSSGContext`](/docs/helpers/ssg#isssgcontext) ヘルパーを使用して、 `` で静的サイト (SSG) を生成する際にストリーミングを無効化できます:
```tsx
app.use(
'*',
jsxRenderer(
({ children }) => {
return (
)
},
(c) => ({
stream: !isSSGContext(c),
})
)
)
```
## ネストされたレイアウト
`Layout` コンポーネントにより、レイアウトをネストできます。
```tsx
app.use(
jsxRenderer(({ children }) => {
return (
{children}
)
})
)
const blog = new Hono()
blog.use(
jsxRenderer(({ children, Layout }) => {
return (
{children}
)
})
)
app.route('/blog', blog)
```
## `useRequestContext()`
`useRequestContext()` は Context のインスタンスを返します。
```tsx
import { useRequestContext, jsxRenderer } from 'hono/jsx-renderer'
const app = new Hono()
app.use(jsxRenderer())
const RequestUrlBadge: FC = () => {
const c = useRequestContext()
return {c.req.url}
}
app.get('/page/info', (c) => {
return c.render(
You are accessing:
)
})
```
::: warning
Deno の `precompile` JSX オプションでは `useRequestContext()` を使用できません。 `react-jsx` を使用してください:
```json
"compilerOptions": {
"jsx": "precompile", // [!code --]
"jsx": "react-jsx", // [!code ++]
"jsxImportSource": "hono/jsx"
}
}
```
:::
## `ContextRenderer` の拡張
以下のように `ContextRenderer` を定義すると、レンダラーに追加のコンテンツを渡せます。 これは例えば、ページごとに head タグの中身を変更したい場合に便利です。
```tsx
declare module 'hono' {
interface ContextRenderer {
(
content: string | Promise,
props: { title: string }
): Response
}
}
const app = new Hono()
app.get(
'/page/*',
jsxRenderer(({ children, title }) => {
return (
{title}
{children}
)
})
)
app.get('/page/favorites', (c) => {
return c.render(
- Eating sushi
- Watching baseball games
,
{
title: 'My favorites',
}
)
})
```
# JWK Auth ミドルウェア
JWK Auth Middleware は、 JWK (JSON Web Key) を使ってトークンを検証することでリクエストを認証します。 `Authorization` ヘッダーと、指定された場合はクッキーなどの他の設定されたソースをチェックします。 提供された `keys` を使用してトークンを検証し、指定された場合は `jwks_uri` からキーを取得し、 `cookie` オプションが設定されている場合はクッキーからのトークン抽出をサポートします。
## このミドルウェアが検証すること
各トークンについて、 `jwk()` は次を行います:
- JWT ヘッダーの形式をパースして検証します。
- `kid` ヘッダーを要求し、 `kid` によってマッチするキーを探します。
- 対称アルゴリズム (`HS256` 、 `HS384` 、 `HS512`) を拒否します。
- ヘッダーの `alg` が設定された `alg` 許可リストに含まれていることを要求します。
- マッチした JWK に `alg` フィールドがある場合、それが JWT ヘッダーの `alg` と一致することを要求します。
- マッチしたキーでトークンの署名を検証します。
- デフォルトでは、時刻ベースのクレームである `nbf` 、 `exp` 、 `iat` を検証します。
オプションのクレーム検証は、 `verification` オプションで設定できます:
- `iss`: 指定された場合に issuer を検証します。
- `aud`: 指定された場合にオーディエンスを検証します。
上記以外の追加のトークンチェックが必要な場合 (例えば、カスタムのアプリケーションレベルの認可ルールなど) は、 `jwk()` の後に自分のミドルウェアで追加してください。
:::info
クライアントから送信される Authorization ヘッダーには、指定されたスキームが必要です。
例: `Bearer my.token.value` または `Basic my.token.value`
:::
## Import
```ts
import { Hono } from 'hono'
import { jwk } from 'hono/jwk'
import { verifyWithJwks } from 'hono/jwt'
```
## 使い方
```ts
const app = new Hono()
app.use(
'/auth/*',
jwk({
jwks_uri: `https://${backendServer}/.well-known/jwks.json`,
alg: ['RS256'],
})
)
app.get('/auth/page', (c) => {
return c.text('You are authorized')
})
```
ペイロードの取得:
```ts
const app = new Hono()
app.use(
'/auth/*',
jwk({
jwks_uri: `https://${backendServer}/.well-known/jwks.json`,
alg: ['RS256'],
})
)
app.get('/auth/page', (c) => {
const payload = c.get('jwtPayload')
return c.json(payload) // eg: { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
})
```
匿名アクセス:
```ts
const app = new Hono()
app.use(
'/auth/*',
jwk({
jwks_uri: (c) =>
`https://${c.env.authServer}/.well-known/jwks.json`,
alg: ['RS256'],
allow_anon: true,
})
)
app.get('/auth/page', (c) => {
const payload = c.get('jwtPayload')
return c.json(payload ?? { message: 'hello anon' })
})
```
## ミドルウェア外での `verifyWithJwks` の使用
`verifyWithJwks` ユーティリティ関数は、 SvelteKit の SSR ページなどの Hono のミドルウェアコンテキスト外のサーバーサイド環境で JWT トークンを検証するために使用できます:
```ts
const id_payload = await verifyWithJwks(
id_token,
{
jwks_uri: 'https://your-auth-server/.well-known/jwks.json',
allowedAlgorithms: ['RS256'],
},
{
cf: { cacheEverything: true, cacheTtl: 3600 },
}
)
```
## JWKS 取得リクエストオプションの設定
`jwks_uri` から JWKS を取得する方法を設定するには、 `jwk()` の第2引数として fetch リクエストオプションを渡します。
この引数は `RequestInit` であり、 JWKS の fetch リクエストにのみ使用されます。
```ts
const app = new Hono()
app.use(
'/auth/*',
jwk(
{
jwks_uri: `https://${backendServer}/.well-known/jwks.json`,
alg: ['RS256'],
},
{
headers: {
Authorization: 'Bearer TOKEN',
},
}
)
)
```
## オプション
### alg: `AsymmetricAlgorithm[]`
トークン検証に使用される許可された非対称アルゴリズムの配列です。
利用可能な型は `RS256` | `RS384` | `RS512` | `PS256` | `PS384` | `PS512` | `ES256` | `ES384` | `ES512` | `EdDSA` です。
### keys: `HonoJsonWebKey[] | (c: Context) => Promise`
公開鍵の値、またはそれらを返す関数です。 関数は Context オブジェクトを受け取ります。
### jwks_uri: `string` | `(c: Context) => Promise`
この値が設定されている場合、この URI から JWK の取得を試みます。 `keys` を含む JSON レスポンスを期待し、取得したキーは提供された `keys` オプションに追加されます。 Context を使用して JWKS URI を動的に決定するコールバック関数を渡すこともできます。
### allow_anon: `boolean`
この値が `true` に設定されている場合、有効なトークンを持たないリクエストもミドルウェアを通過できるようになります。 リクエストが認証されているかどうかを確認するには `c.get('jwtPayload')` を使用してください。 デフォルトは `false` です。
### cookie: `string`
この値が設定されている場合、その値をキーとしてクッキーヘッダーから値が取得され、トークンとして検証されます。
### headerName: `string`
JWT トークンを探すヘッダーの名前です。 デフォルトは `Authorization` です。
### realm: `string`
`401` レスポンスで返される `WWW-Authenticate` チャレンジヘッダーの `realm` パラメータによって記述される保護空間です。 デフォルトはリクエスト URL です。
### verification: `VerifyOptions`
署名検証に加えて、クレーム検証の挙動を設定します:
[Keep in sync with jwt.md]: #
#### VerifyOptions.iss: `string | RegExp`
トークン検証に使用される期待される issuer です。 これが設定されていない場合、 `iss` クレームはチェック**されません**。
#### VerifyOptions.aud: `string | string[] | RegExp`
トークン検証に使用される期待されるオーディエンスです。 これが設定されている場合、トークンは `aud` クレームを含んでいなければならず、少なくとも1つのオーディエンス値が一致する必要があります。
#### VerifyOptions.nbf: `boolean`
`nbf` (not before) クレームは、存在し、かつこれが `true` に設定されている場合に検証されます。 デフォルトは `true` です。
#### VerifyOptions.iat: `boolean`
`iat` (issued at) クレームは、存在し、かつこれが `true` に設定されている場合に検証されます。 デフォルトは `true` です。
#### VerifyOptions.exp: `boolean`
`exp` (expiration time) クレームは、存在し、かつこれが `true` に設定されている場合に検証されます。 デフォルトは `true` です。
# JWT 認証ミドルウェア
JWT 認証ミドルウェアは、 JWT を使ってトークンを検証して認証することができます。
ミドルウェアは、 `cookie` オプションがセットされていない場合に `Authorization` ヘッダを確認します。 ヘッダの名前は `headerName` オプションでカスタマイズすることができます。
:::info
クライアントから送信された Authotization ヘッダには、指定されたスキーマが必要です。
例: `Bearer my.token.value` や `Basic my.token.value` など
:::
## Import
```ts
import { Hono } from 'hono'
import { jwt } from 'hono/jwt'
import type { JwtVariables } from 'hono/jwt'
```
## 使い方
```ts
// Specify the variable types to infer the `c.get('jwtPayload')`:
type Variables = JwtVariables
const app = new Hono<{ Variables: Variables }>()
app.use(
'/auth/*',
jwt({
secret: 'it-is-very-secret',
alg: 'HS256',
})
)
app.get('/auth/page', (c) => {
return c.text('You are authorized')
})
```
ペイロードを取得する:
```ts
const app = new Hono()
app.use(
'/auth/*',
jwt({
secret: 'it-is-very-secret',
alg: 'HS256',
verification: {
iss: 'my-trusted-issuer',
aud: 'my-api',
},
})
)
app.get('/auth/page', (c) => {
const payload = c.get('jwtPayload')
return c.json(payload) // eg: { "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "iss": "my-trusted-issuer" }
})
```
::: tip
`jwt()` は単なるミドルウェア関数です。 環境変数を使いたい場合は、 (例: `c.env.JWT_SECRET`) 、 以下のように使用できます:
```js
app.use('/auth/*', (c, next) => {
const jwtMiddleware = jwt({
secret: c.env.JWT_SECRET,
alg: 'HS256',
})
return jwtMiddleware(c, next)
})
```
:::
## オプション
### secret: `string`
秘密鍵の値。
### alg: `string`
認証に使われるアルゴリズム。
利用可能な型は `HS256` | `HS384` | `HS512` | `RS256` | `RS384` | `RS512` | `PS256` | `PS384` | `PS512` | `ES256` | `ES384` | `ES512` | `EdDSA` です。
### cookie: `string`
この値が設定されている場合は、 Cookie ヘッダから値が取得され、検証されます。
### headerName: `string`
JWT トークンを探すヘッダの名前。 デフォルトは `Authorization` です。
```ts
app.use(
'/auth/*',
jwt({
secret: 'it-is-very-secret',
alg: 'HS256',
headerName: 'x-custom-auth-header',
})
)
```
### realm: `string`
`401` レスポンスで返される `WWW-Authenticate` チャレンジヘッダーの `realm` パラメータによって記述される保護空間です。 デフォルトはリクエスト URL です。
```ts
app.use(
'/auth/*',
jwt({
secret: 'it-is-very-secret',
realm: 'my-protected-api',
})
)
```
### verification: `VerifyOptions`
トークンの検証を制御するオプション。
[Keep in sync with jwk.md]: #
#### VerifyOptions.iss: `string | RegExp`
期待される検証トークンの発行者。 この値が設定されていない場合、 `iss` クレームはチェック**されません**。
#### VerifyOptions.aud: `string | string[] | RegExp`
トークン検証に使用される期待されるオーディエンスです。 これが設定されている場合、トークンは `aud` クレームを含んでいなければならず、少なくとも1つのオーディエンス値が一致する必要があります。
#### VerifyOptions.nbf: `boolean`
`nbf` (not before) クレームは、存在し、かつこれが `true` に設定されている場合に検証されます。 デフォルトは `true` です。
#### VerifyOptions.iat: `boolean`
`iat` (issued at) クレームは、存在し、かつこれが `true` に設定されている場合に検証されます。 デフォルトは `true` です。
#### VerifyOptions.exp: `boolean`
`exp` (expiration time) クレームは、存在し、かつこれが `true` に設定されている場合に検証されます。 デフォルトは `true` です。
# Language ミドルウェア
Language Detector ミドルウェアは、様々なソースからユーザーの優先言語 (ロケール) を自動的に判定し、 `c.get('language')` 経由で利用できるようにします。 検出戦略には、クエリパラメータ、クッキー、ヘッダー、 URL パスセグメントが含まれます。 国際化 (i18n) やロケール固有のコンテンツに最適です。
## Import
```ts
import { Hono } from 'hono'
import { languageDetector } from 'hono/language'
```
## 基本的な使い方
クエリ文字列、クッキー、ヘッダー (デフォルトの順序) から言語を検出し、フォールバックとして英語を使用します:
```ts
const app = new Hono()
app.use(
languageDetector({
supportedLanguages: ['en', 'ar', 'ja'], // Must include fallback
fallbackLanguage: 'en', // Required
})
)
app.get('/', (c) => {
const lang = c.get('language')
return c.text(`Hello! Your language is ${lang}`)
})
```
### クライアントの例
```sh
# Via path
curl http://localhost:8787/ar/home
# Via query parameter
curl http://localhost:8787/?lang=ar
# Via cookie
curl -H 'Cookie: language=ja' http://localhost:8787/
# Via header
curl -H 'Accept-Language: ar,en;q=0.9' http://localhost:8787/
```
## デフォルト設定
```ts
export const DEFAULT_OPTIONS: DetectorOptions = {
order: ['querystring', 'cookie', 'header'],
lookupQueryString: 'lang',
lookupCookie: 'language',
lookupFromHeaderKey: 'accept-language',
lookupFromPathIndex: 0,
caches: ['cookie'],
ignoreCase: true,
fallbackLanguage: 'en',
supportedLanguages: ['en'],
cookieOptions: {
sameSite: 'Strict',
secure: true,
maxAge: 365 * 24 * 60 * 60,
httpOnly: true,
},
debug: false,
}
```
## 主な挙動
### 検出のワークフロー
1. **Order**: デフォルトではこの順序でソースをチェックします:
- クエリパラメータ (?lang=ar)
- クッキー (language=ar)
- Accept-Language ヘッダー
2. **Caching**: 検出された言語をクッキーに保存します (デフォルトで1年)
3. **Fallback**: 有効な検出がなかった場合に `fallbackLanguage` を使用します (`supportedLanguages` に含まれている必要があります)
## 高度な設定
### カスタム検出順序
URL パスからの検出を優先します (例: /en/about):
```ts
app.use(
languageDetector({
order: ['path', 'cookie', 'querystring', 'header'],
lookupFromPathIndex: 0, // /en/profile → index 0 = 'en'
supportedLanguages: ['en', 'ar'],
fallbackLanguage: 'en',
})
)
```
### 段階的なロケールマッチング
`ja-JP` のような検出されたロケールコードが `supportedLanguages` に存在しない場合、ミドルウェアはサブタグを段階的に切り詰めてマッチを探します。 例えば、 `zh-Hant-CN` は `zh-Hant` 、次に `zh` を試します。 完全一致が常に優先されます。
```ts
app.use(
languageDetector({
supportedLanguages: ['en', 'ja', 'zh-Hant'],
fallbackLanguage: 'en',
})
)
// Accept-Language: ja-JP → matches 'ja'
// Accept-Language: zh-Hant-CN → matches 'zh-Hant'
```
### 言語コードの変換
複雑なコードを正規化します (例: en-US → en):
```ts
app.use(
languageDetector({
convertDetectedLanguage: (lang) => lang.split('-')[0],
supportedLanguages: ['en', 'ja'],
fallbackLanguage: 'en',
})
)
```
### クッキーの設定
```ts
app.use(
languageDetector({
lookupCookie: 'app_lang',
caches: ['cookie'],
cookieOptions: {
path: '/', // Cookie path
sameSite: 'Lax', // Cookie same-site policy
secure: true, // Only send over HTTPS
maxAge: 86400 * 365, // 1 year expiration
httpOnly: true, // Not accessible via JavaScript
domain: '.example.com', // Optional: specific domain
},
})
)
```
クッキーキャッシュを無効にする場合:
```ts
languageDetector({
caches: false,
})
```
### デバッグ
検出の過程をログに出力します:
```ts
languageDetector({
debug: true, // Shows: "Detected from querystring: ar"
})
```
## オプションリファレンス
### 基本オプション
| Option | Type | Default | Required | Description |
| :------------------- | :--------------- | :------------------------------------ | :------- | :--------------------- |
| `supportedLanguages` | `string[]` | `['en']` | Yes | Allowed language codes |
| `fallbackLanguage` | `string` | `'en'` | Yes | Default language |
| `order` | `DetectorType[]` | `['querystring', 'cookie', 'header']` | No | Detection sequence |
| `debug` | `boolean` | `false` | No | Enable logging |
### 検出オプション
| Option | Type | Default | Description |
| :-------------------- | :------- | :------------------ | :------------------- |
| `lookupQueryString` | `string` | `'lang'` | Query parameter name |
| `lookupCookie` | `string` | `'language'` | Cookie name |
| `lookupFromHeaderKey` | `string` | `'accept-language'` | Header name |
| `lookupFromPathIndex` | `number` | `0` | Path segment index |
### クッキーオプション
| Option | Type | Default | Description |
| :----------------------- | :---------------------------- | :----------- | :------------------- |
| `caches` | `CacheType[] \| false` | `['cookie']` | Cache settings |
| `cookieOptions.path` | `string` | `'/'` | Cookie path |
| `cookieOptions.sameSite` | `'Strict' \| 'Lax' \| 'None'` | `'Strict'` | SameSite policy |
| `cookieOptions.secure` | `boolean` | `true` | HTTPS only |
| `cookieOptions.maxAge` | `number` | `31536000` | Expiration (seconds) |
| `cookieOptions.httpOnly` | `boolean` | `true` | JS accessibility |
| `cookieOptions.domain` | `string` | `undefined` | Cookie domain |
### 高度なオプション
| Option | Type | Default | Description |
| :------------------------ | :------------------------- | :---------- | :------------------------ |
| `ignoreCase` | `boolean` | `true` | Case-insensitive matching |
| `convertDetectedLanguage` | `(lang: string) => string` | `undefined` | Language code transformer |
## バリデーションとエラー処理
- `fallbackLanguage` は `supportedLanguages` に含まれている必要があります (セットアップ時にエラーをスローします)
- `lookupFromPathIndex` は 0 以上である必要があります
- 無効な設定はミドルウェアの初期化時にエラーをスローします
- 検出に失敗した場合は、黙って `fallbackLanguage` が使用されます
## よくあるレシピ
### パスベースのルーティング
```ts
app.get('/:lang/home', (c) => {
const lang = c.get('language') // 'en', 'ar', etc.
return c.json({ message: getLocalizedContent(lang) })
})
```
### 複数のサポート言語
```ts
languageDetector({
supportedLanguages: ['en', 'en-GB', 'ar', 'ar-EG'],
convertDetectedLanguage: (lang) => lang.replace('_', '-'), // Normalize
})
```
# Logger ミドルウェア
シンプルなロガーです。
## Import
```ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
```
## 使い方
```ts
const app = new Hono()
app.use(logger())
app.get('/', (c) => c.text('Hello Hono!'))
```
## ログの詳細
Logger Middleware は、各リクエストについて以下の詳細をログに出力します:
- **Incoming Request**: HTTP メソッド、リクエストパス、受信したリクエストをログに出力します。
- **Outgoing Response**: HTTP メソッド、リクエストパス、レスポンスのステータスコード、リクエスト/レスポンスの時間をログに出力します。
- **Status Code Coloring**: レスポンスのステータスコードは、視認性を高め、ステータスカテゴリを素早く識別できるように色分けされます。 異なるステータスコードのカテゴリは、異なる色で表現されます。
- **Elapsed Time**: リクエスト/レスポンスのサイクルにかかった時間は、人間が読める形式で、ミリ秒 (ms) または秒 (s) のいずれかでログに出力されます。
Logger Middleware を使用することで、 Hono アプリケーションにおけるリクエストとレスポンスの流れを簡単に監視し、問題やパフォーマンスのボトルネックを素早く特定できます。
独自の `PrintFunc` 関数を提供することで、ログの挙動をカスタマイズし、ミドルウェアをさらに拡張することもできます。
::: tip
_status code coloring_ を無効にするには、 `NO_COLOR` 環境変数を設定します。 これは、ロギングライブラリで ANSI カラーエスケープコードを無効にする一般的な方法で、 で説明されています。 なお、 Cloudflare Workers には `process.env` オブジェクトがないため、デフォルトではプレーンテキストのログ出力になります。
:::
## PrintFunc
Logger Middleware は、オプションの `PrintFunc` 関数をパラメータとして受け取ります。 この関数により、ロガーをカスタマイズしたり、追加のログを出力したりできます。
## オプション
### fn: `PrintFunc(str: string, ...rest: string[])`
- `str`: ロガーから渡されます。
- `...rest`: コンソールに出力される追加の文字列 props です。
### 例
Logger Middleware にカスタム `PrintFunc` 関数を設定します:
```ts
export const customLogger = (message: string, ...rest: string[]) => {
console.log(message, ...rest)
}
app.use(logger(customLogger))
```
ルート内でカスタムロガーを使用します:
```ts
app.post('/blog', (c) => {
// Routing logic
customLogger('Blog saved:', `Path: ${blog.url},`, `ID: ${blog.id}`)
// Output
// <-- POST /blog
// Blog saved: Path: /blog/example, ID: 1
// --> POST /blog 201 93ms
// Return Context
})
```
# Method Not Allowed ミドルウェア
Method Not Allowed ミドルウェアは、リクエストパスが登録済みのルートにマッチするが、リクエストメソッドがサポートされていない場合に、 `Allow` ヘッダー付きの `405 Method Not Allowed` レスポンスを返します。 このミドルウェアがない場合、 Hono はそのケースで `404 Not Found` を返します。
## Import
```ts
import { Hono } from 'hono'
import { methodNotAllowed } from 'hono/method-not-allowed'
```
## 使い方
```ts
const app = new Hono()
app.use(methodNotAllowed({ app }))
app.get('/hello', (c) => c.text('Hello!'))
app.post('/hello', (c) => c.text('Posted!'))
// PUT /hello -> 405 Method Not Allowed
// Allow: GET, HEAD, POST
```
`onMethodNotAllowed` オプションでレスポンスをカスタマイズできます:
```ts
app.use(
methodNotAllowed({
app,
onMethodNotAllowed: (c, methods) =>
c.json({ error: 'Method Not Allowed' }, 405, {
Allow: methods.join(', '),
}),
})
)
```
## オプション
### app: `Hono`
アプリケーションで使用される Hono のインスタンスです。 ミドルウェアは、登録されたルートから各パスに対して許可されたメソッドを収集します。
### onMethodNotAllowed: `(c: Context, allowedMethods: string[]) => Response | Promise`
`Allow` ヘッダーを含むレスポンスを生成します。 デフォルトでは、ミドルウェアは許可されたメソッドが設定された `Allow` ヘッダー付きの `405 Method Not Allowed` レスポンスを返します。
# Method Override ミドルウェア
このミドルウェアは、フォーム、ヘッダー、クエリの値に応じて、リクエストの実際のメソッドとは異なる指定されたメソッドのハンドラーを実行し、そのレスポンスを返します。
## Import
```ts
import { Hono } from 'hono'
import { methodOverride } from 'hono/method-override'
```
## 使い方
```ts
const app = new Hono()
// If no options are specified, the value of `_method` in the form,
// e.g. DELETE, is used as the method.
app.use('/posts', methodOverride({ app }))
app.delete('/posts', (c) => {
// ....
})
```
## 例
HTML フォームは DELETE メソッドを送信できないため、 `_method` という名前のプロパティに `DELETE` の値を入れて送信できます。 すると、 `app.delete()` のハンドラーが実行されます。
HTML フォーム:
```html
```
アプリケーション:
```ts
import { methodOverride } from 'hono/method-override'
const app = new Hono()
app.use('/posts', methodOverride({ app }))
app.delete('/posts', () => {
// ...
})
```
デフォルト値を変更したり、ヘッダー値やクエリ値を使用できます:
```ts
app.use('/posts', methodOverride({ app, form: '_custom_name' }))
app.use(
'/posts',
methodOverride({ app, header: 'X-METHOD-OVERRIDE' })
)
app.use('/posts', methodOverride({ app, query: '_method' }))
```
## オプション
### app: `Hono`
アプリケーションで使用される `Hono` のインスタンスです。
### form: `string`
メソッド名を含む値を持つフォームのキーです。
デフォルトは `_method` です。
### header: `boolean`
メソッド名を含む値を持つヘッダー名です。
### query: `boolean`
メソッド名を含む値を持つクエリパラメータのキーです。
# Mount ミドルウェア
Mount ミドルウェアは、 別のフレームワークでビルドされたアプリケーションを自身の Hono アプリケーションへマウントさせます。
## Import
```ts twoslash
import { Hono } from 'hono'
import { mount } from 'hono/mount'
```
## 使い方
`app.all()` を使用して `/*` で終わるパスに対してミドルウェアを登録します:
```ts
import { Router as IttyRouter } from 'itty-router'
// itty-router アプリケーションを生成
const ittyRouter = IttyRouter()
// `GET /itty-router/hello` を処理
ittyRouter.get('/hello', () => new Response('Hello from itty-router'))
const app = new Hono()
app.all('/itty-router/*', mount(ittyRouter.handle))
```
デフォルトでは、 マウントされたアプリケーションは、 URL からプレフィクスが削除されたパスを持った新しい `Request` オブジェクトを受け取ります。 `/itty-router/hello` へのリクエストは、 `/hello` として itty-router へ渡されます。
マウントされたアプリケーションは、 `handler(request, c.env, c.executionCtx)` のようにコールされます。 Cloudflare Workers `fetch` ハンドラと同じ引数です。 別の引数を渡すには `optionHandler` を使用します。
## オプション
第 2 引数として関数やオブジェクトを渡すことができます。
### optionHandler: `(c: Context) => unknown`
`Request` の後にマウントされたアプリケーションに渡した引数を返します。 配列は複数の引数に展開され、 第 2 引数として直接関数を渡すことができます:
```ts twoslash
import { Hono } from 'hono'
import { mount } from 'hono/mount'
const app = new Hono()
const anotherApp = (request: Request, ...args: unknown[]) =>
new Response(request.url)
// ---cut---
// `anotherApp(request, c.env)` をコール
app.all(
'/app/*',
mount(anotherApp, (c) => c.env)
)
// `anotherApp(request, c.env, c.executionCtx)` をコール
app.all(
'/app/*',
mount(anotherApp, {
optionHandler: (c) => [c.env, c.executionCtx],
})
)
```
### replaceRequest: `((originalRequest: Request) => Request) | false`
どの `Request` がマウントされたアプリケーションに渡されたのかを制御します:
```ts twoslash
import { Hono } from 'hono'
import { mount } from 'hono/mount'
const app = new Hono()
const anotherApp = (request: Request, ...args: unknown[]) =>
new Response(request.url)
// ---cut---
// `/legacy/users` は `/api/users` としてマウントされたアプリケーションに届きます
app.all(
'/legacy/*',
mount(anotherApp, {
replaceRequest: (req) => {
const url = new URL(req.url)
url.pathname = url.pathname.replace(/^\/legacy/, '/api')
return new Request(url, req)
},
})
)
```
変更されていない元の `Request` を渡す際に、 `(req) => req` を簡略化するために `false` をセットします:
```ts twoslash
import { Hono } from 'hono'
import { mount } from 'hono/mount'
const app = new Hono()
const anotherApp = (request: Request, ...args: unknown[]) =>
new Response(request.url)
// ---cut---
app.all(
'/app/*',
mount(anotherApp, {
replaceRequest: false,
})
)
```
# Pretty JSON ミドルウェア
Pretty JSON ミドルウェアは、 JSON レスポンスボディに対して "_JSON pretty print_" を有効にします。
URL のクエリパラメータに `?pretty` を追加すると、 JSON 文字列が整形されます。
```js
// GET /
{"project":{"name":"Hono","repository":"https://github.com/honojs/hono"}}
```
これは次のようになります:
```js
// GET /?pretty
{
"project": {
"name": "Hono",
"repository": "https://github.com/honojs/hono"
}
}
```
## Import
```ts
import { Hono } from 'hono'
import { prettyJSON } from 'hono/pretty-json'
```
## 使い方
```ts
const app = new Hono()
app.use(prettyJSON()) // With options: prettyJSON({ space: 4 })
app.get('/', (c) => {
return c.json({ message: 'Hono!' })
})
```
## オプション
### space: `number`
インデントに使用するスペースの数です。 デフォルトは `2` です。
### query: `string`
適用するためのクエリ文字列の名前です。 デフォルトは `pretty` です。
### force: `boolean`
`true` に設定すると、クエリパラメータに関係なく JSON レスポンスが常に整形されます。 デフォルトは `false` です。
# Request ID ミドルウェア
Request ID Middleware は、各リクエストに対して一意の ID を生成し、ハンドラー内で使用できます。
::: info
**Node.js**: このミドルウェアは `crypto.randomUUID()` を使って ID を生成します。 グローバルな `crypto` は Node.js のバージョン 20 以降で導入されました。 そのため、それより前のバージョンではエラーが発生する可能性があります。 その場合は `generator` を指定してください。 ただし、 [Node.js アダプタ](https://github.com/honojs/node-server) を使用している場合は、 `crypto` が自動的にグローバルに設定されるため、この必要はありません。
:::
## Import
```ts
import { Hono } from 'hono'
import { requestId } from 'hono/request-id'
```
## 使い方
Request ID Middleware が適用されたハンドラーやミドルウェアでは、 `requestId` 変数を通して Request ID にアクセスできます。
```ts
const app = new Hono()
app.use('*', requestId())
app.get('/', (c) => {
return c.text(`Your request id is ${c.get('requestId')}`)
})
```
型を明示的に指定したい場合は、 `RequestIdVariables` をインポートして、 `new Hono()` のジェネリクスに渡します。
```ts
import type { RequestIdVariables } from 'hono/request-id'
const app = new Hono<{
Variables: RequestIdVariables
}>()
```
### Request ID の設定
ヘッダー (デフォルト: `X-Request-Id`) にカスタム Request ID を設定すると、ミドルウェアは新しい ID を生成する代わりにその値を使用します:
```ts
const app = new Hono()
app.use('*', requestId())
app.get('/', (c) => {
return c.text(`${c.get('requestId')}`)
})
const res = await app.request('/', {
headers: {
'X-Request-Id': 'your-custom-id',
},
})
console.log(await res.text()) // your-custom-id
```
この機能を無効にしたい場合は、 [`headerName` オプション](#headername-string) に空文字列を設定してください。
## オプション
### limitLength: `number`
Request ID の最大長です。 デフォルトは `255` です。
### headerName: `string`
Request ID に使用されるヘッダー名です。 デフォルトは `X-Request-Id` です。
### generator: `(c: Context) => string`
Request ID の生成関数です。 デフォルトでは `crypto.randomUUID()` を使用します。
## プラットフォーム固有の Request ID
一部のプラットフォーム (AWS Lambda など) は、リクエストごとに独自の Request ID を既に生成しています。
追加の設定を行わない場合、このミドルウェアはこれらのプラットフォーム固有の Request ID を認識せず、
新しい Request ID を生成します。 これにより、アプリケーションのログを見るときに混乱が生じる可能性があります。
これらの ID を統一するには、 `generator` 関数を使ってプラットフォーム固有の Request ID を取得し、このミドルウェアで使用します。
### プラットフォーム固有のリンク
- AWS Lambda
- [AWS documentation: Context object](https://docs.aws.amazon.com/lambda/latest/dg/nodejs-context.html)
- [Hono: Access AWS Lambda Object](/docs/getting-started/aws-lambda#access-aws-lambda-object)
- Cloudflare
- [Cloudflare Ray ID
](https://developers.cloudflare.com/fundamentals/reference/cloudflare-ray-id/)
- Deno
- [Request ID on the Deno Blog](https://deno.com/blog/zero-config-debugging-deno-opentelemetry#:~:text=s%20automatically%20have-,unique%20request%20IDs,-associated%20with%20them)
- Fastly
- [Fastly documentation: req.xid](https://www.fastly.com/documentation/reference/vcl/variables/client-request/req-xid/)
# Secure Headers ミドルウェア
Secure Headers Middleware は、セキュリティヘッダーの設定を簡単にします。 Helmet の機能に一部インスピレーションを受けており、特定のセキュリティヘッダーの有効化と無効化を制御できます。
## Import
```ts
import { Hono } from 'hono'
import { secureHeaders } from 'hono/secure-headers'
```
## 使い方
デフォルトで、最適な設定を使用できます。
```ts
const app = new Hono()
app.use(secureHeaders())
```
不要なヘッダーは、 `false` を設定することで抑制できます。
```ts
const app = new Hono()
app.use(
'*',
secureHeaders({
xFrameOptions: false,
xXssProtection: false,
})
)
```
デフォルトのヘッダー値は、文字列で上書きできます。
```ts
const app = new Hono()
app.use(
'*',
secureHeaders({
strictTransportSecurity:
'max-age=63072000; includeSubDomains; preload',
xFrameOptions: 'DENY',
xXssProtection: '1',
})
)
```
## サポートされているオプション
各オプションは、次のヘッダーのキーと値のペアに対応しています。
| Option | Header | Value | Default |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ---------- |
| - | X-Powered-By | (Delete Header) | True |
| contentSecurityPolicy | [Content-Security-Policy](https://developer.mozilla.org/ja/docs/Web/HTTP/CSP) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
| contentSecurityPolicyReportOnly | [Content-Security-Policy-Report-Only](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
| trustedTypes | [Trusted Types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
| requireTrustedTypesFor | [Require Trusted Types For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/require-trusted-types-for) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
| crossOriginEmbedderPolicy | [Cross-Origin-Embedder-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) | require-corp | **False** |
| crossOriginResourcePolicy | [Cross-Origin-Resource-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy) | same-origin | True |
| crossOriginOpenerPolicy | [Cross-Origin-Opener-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) | same-origin | True |
| originAgentCluster | [Origin-Agent-Cluster](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin-Agent-Cluster) | ?1 | True |
| referrerPolicy | [Referrer-Policy](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Referrer-Policy) | no-referrer | True |
| reportingEndpoints | [Reporting-Endpoints](https://www.w3.org/TR/reporting-1/#header) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
| reportTo | [Report-To](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-to) | Usage: [Setting Content-Security-Policy](#setting-content-security-policy) | No Setting |
| strictTransportSecurity | [Strict-Transport-Security](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Strict-Transport-Security) | max-age=15552000; includeSubDomains | True |
| xContentTypeOptions | [X-Content-Type-Options](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Content-Type-Options) | nosniff | True |
| xDnsPrefetchControl | [X-DNS-Prefetch-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control) | off | True |
| xDownloadOptions | [X-Download-Options](https://learn.microsoft.com/en-us/archive/blogs/ie/ie8-security-part-v-comprehensive-protection#mime-handling-force-save) | noopen | True |
| xFrameOptions | [X-Frame-Options](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Frame-Options) | SAMEORIGIN | True |
| xPermittedCrossDomainPolicies | [X-Permitted-Cross-Domain-Policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Permitted-Cross-Domain-Policies) | none | True |
| xXssProtection | [X-XSS-Protection](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-XSS-Protection) | 0 | True |
| permissionPolicy | [Permissions-Policy](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Permissions-Policy) | Usage: [Setting Permission-Policy](#setting-permission-policy) | No Setting |
## Middleware Conflict
同じヘッダーを操作するミドルウェアを扱う場合は、指定の順序に注意してください。
この場合、 Secure-headers が動作し、 `x-powered-by` は削除されます:
```ts
const app = new Hono()
app.use(secureHeaders())
app.use(poweredBy())
```
この場合、 Powered-By が動作し、 `x-powered-by` は追加されます:
```ts
const app = new Hono()
app.use(poweredBy())
app.use(secureHeaders())
```
## Content-Security-Policy の設定
```ts
const app = new Hono()
app.use(
'/test',
secureHeaders({
reportingEndpoints: [
{
name: 'endpoint-1',
url: 'https://example.com/reports',
},
],
// -- or alternatively
// reportTo: [
// {
// group: 'endpoint-1',
// max_age: 10886400,
// endpoints: [{ url: 'https://example.com/reports' }],
// },
// ],
contentSecurityPolicy: {
defaultSrc: ["'self'"],
baseUri: ["'self'"],
childSrc: ["'self'"],
connectSrc: ["'self'"],
fontSrc: ["'self'", 'https:', 'data:'],
formAction: ["'self'"],
frameAncestors: ["'self'"],
frameSrc: ["'self'"],
imgSrc: ["'self'", 'data:'],
manifestSrc: ["'self'"],
mediaSrc: ["'self'"],
objectSrc: ["'none'"],
reportTo: 'endpoint-1',
reportUri: '/csp-report',
sandbox: ['allow-same-origin', 'allow-scripts'],
scriptSrc: ["'self'"],
scriptSrcAttr: ["'none'"],
scriptSrcElem: ["'self'"],
styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
styleSrcAttr: ['none'],
styleSrcElem: ["'self'", 'https:', "'unsafe-inline'"],
upgradeInsecureRequests: [],
workerSrc: ["'self'"],
},
})
)
```
### `nonce` 属性
`hono/secure-headers` からインポートした `NONCE` を `scriptSrc` または `styleSrc` に追加することで、 `script` または `style` 要素に [`nonce` 属性](https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/nonce) を追加できます:
```tsx
import { secureHeaders, NONCE } from 'hono/secure-headers'
import type { SecureHeadersVariables } from 'hono/secure-headers'
// Specify the variable types to infer the `c.get('secureHeadersNonce')`:
type Variables = SecureHeadersVariables
const app = new Hono<{ Variables: Variables }>()
// Set the pre-defined nonce value to `scriptSrc`:
app.get(
'*',
secureHeaders({
contentSecurityPolicy: {
scriptSrc: [NONCE, 'https://allowed1.example.com'],
},
})
)
// Get the value from `c.get('secureHeadersNonce')`:
app.get('/', (c) => {
return c.html(
{/** contents */}
)
})
```
nonce 値を自分で生成したい場合は、次のように関数を指定することもできます:
```tsx
const app = new Hono<{
Variables: { myNonce: string }
}>()
const myNonceGenerator: ContentSecurityPolicyOptionHandler = (c) => {
// This function is called on every request.
const nonce = Math.random().toString(36).slice(2)
c.set('myNonce', nonce)
return `'nonce-${nonce}'`
}
app.get(
'*',
secureHeaders({
contentSecurityPolicy: {
scriptSrc: [myNonceGenerator, 'https://allowed1.example.com'],
},
})
)
app.get('/', (c) => {
return c.html(
{/** contents */}
)
})
```
## Permission-Policy の設定
Permission-Policy ヘッダーにより、ブラウザでどの機能や API が使用できるかを制御できます。 設定方法の例は次の通りです:
```ts
const app = new Hono()
app.use(
'*',
secureHeaders({
permissionsPolicy: {
fullscreen: ['self'], // fullscreen=(self)
bluetooth: ['none'], // bluetooth=(none)
payment: ['self', 'https://example.com'], // payment=(self "https://example.com")
syncXhr: [], // sync-xhr=()
camera: false, // camera=none
microphone: true, // microphone=*
geolocation: ['*'], // geolocation=*
usb: ['self', 'https://a.example.com', 'https://b.example.com'], // usb=(self "https://a.example.com" "https://b.example.com")
accelerometer: ['https://*.example.com'], // accelerometer=("https://*.example.com")
gyroscope: ['src'], // gyroscope=(src)
magnetometer: [
'https://a.example.com',
'https://b.example.com',
], // magnetometer=("https://a.example.com" "https://b.example.com")
},
})
)
```
# Timeout ミドルウェア
Timeout Middleware を使うと、アプリケーション内のリクエストタイムアウトを簡単に管理できます。 リクエストの最大継続時間を設定し、指定されたタイムアウトを超えた場合にカスタムエラーレスポンスを定義することもできます。
## Import
```ts
import { Hono } from 'hono'
import { timeout } from 'hono/timeout'
```
## 使い方
デフォルト設定とカスタム設定の両方を使った Timeout Middleware の使用方法は次の通りです:
デフォルト設定:
```ts
const app = new Hono()
// Applying a 5-second timeout
app.use('/api', timeout(5000))
// Handling a route
app.get('/api/data', async (c) => {
// Your route handler logic
return c.json({ data: 'Your data here' })
})
```
カスタム設定:
```ts
import { HTTPException } from 'hono/http-exception'
// Custom exception factory function
const customTimeoutException = (context) =>
new HTTPException(408, {
message: `Request timeout after waiting ${context.req.headers.get(
'Duration'
)} seconds. Please try again later.`,
})
// for Static Exception Message
// const customTimeoutException = new HTTPException(408, {
// message: 'Operation timed out. Please try again later.'
// });
// Applying a 1-minute timeout with a custom exception
app.use('/api/long-process', timeout(60000, customTimeoutException))
app.get('/api/long-process', async (c) => {
// Simulate a long process
await new Promise((resolve) => setTimeout(resolve, 61000))
return c.json({ data: 'This usually takes longer' })
})
```
## Notes
- タイムアウトの時間はミリ秒で指定できます。 指定された時間を超えると、ミドルウェアは自動的に promise を reject し、場合によってはエラーをスローします。
- Timeout Middleware はストリームと併用できません。 そのため、 `stream.close` と `setTimeout` を組み合わせて使用してください。
```ts
app.get('/sse', async (c) => {
let id = 0
let running = true
let timer: number | undefined
return streamSSE(c, async (stream) => {
timer = setTimeout(() => {
console.log('Stream timeout reached, closing stream')
stream.close()
}, 3000) as unknown as number
stream.onAbort(async () => {
console.log('Client closed connection')
running = false
clearTimeout(timer)
})
while (running) {
const message = `It is ${new Date().toISOString()}`
await stream.writeSSE({
data: message,
event: 'time-update',
id: String(id++),
})
await stream.sleep(1000)
}
})
})
```
## Middleware Conflicts
エラー処理やその他のタイミング関連のミドルウェアを使用する場合は、この Timeout Middleware の挙動に影響を与える可能性があるため、ミドルウェアの順序に注意してください。
# Server-Timing ミドルウェア
[Server-Timing](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Server-Timing) Middleware は、
レスポンスヘッダーにパフォーマンスメトリクスを提供します。
::: info
Note: Cloudflare Workers では、 [タイマーは最後の I/O の時間のみを示す](https://developers.cloudflare.com/workers/learning/security-model/#step-1-disallow-timers-and-multi-threading) ため、
タイマーのメトリクスは正確でない場合があります。
:::
## Import
```ts [npm]
import { Hono } from 'hono'
import {
timing,
setMetric,
startTime,
endTime,
wrapTime,
} from 'hono/timing'
import type { TimingVariables } from 'hono/timing'
```
## 使い方
```js
// Specify the variable types to infer the `c.get('metric')`:
type Variables = TimingVariables
const app = new Hono<{ Variables: Variables }>()
// add the middleware to your router
app.use(timing());
app.get('/', async (c) => {
// add custom metrics
setMetric(c, 'region', 'europe-west3')
// add custom metrics with timing, must be in milliseconds
setMetric(c, 'custom', 23.8, 'My custom Metric')
// start a new timer
startTime(c, 'db');
const data = await db.findMany(...);
// end the timer
endTime(c, 'db');
// ...or you can also just wrap a Promise using this function:
const data = await wrapTime(c, 'db', db.findMany(...));
return c.json({ response: data });
});
```
### 条件付きで有効化
```ts
const app = new Hono()
app.use(
'*',
timing({
// c: Context of the request
enabled: (c) => c.req.method === 'POST',
})
)
```
## 結果

## オプション
### total: `boolean`
レスポンスの合計時間を表示します。 デフォルトは `true` です。
### enabled: `boolean` | `(c: Context) => boolean`
ヘッダーにタイミングを追加するかどうかです。 デフォルトは `true` です。
### totalDescription: `string`
レスポンスの合計時間の説明です。 デフォルトは `Total Response Time` です。
### autoEnd: `boolean`
`startTime()` をリクエストの終了時に自動的に終了させるかどうかです。
無効にした場合、手動で終了されていないタイマーは表示されません。
### crossOrigin: `boolean` | `string` | `(c: Context) => boolean | string`
このタイミングヘッダーが読み取り可能なオリジンです。
- false の場合、現在のオリジンからのみ。
- true の場合、すべてのオリジンから。
- 文字列の場合、そのドメイン (複数可) から。 複数のドメインはカンマで区切る必要があります。
デフォルトは `false` です。 詳しくは [ドキュメント](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Timing-Allow-Origin) をご覧ください。
# Trailing Slash ミドルウェア
このミドルウェアは、 GET リクエストの URL 内の Trailing Slash を処理します。
`appendTrailingSlash` は、コンテンツが見つからなかった場合に、 Trailing Slash を追加した URL へリダイレクトします。 また、 `trimTrailingSlash` は Trailing Slash を削除します。
## Import
```ts
import { Hono } from 'hono'
import {
appendTrailingSlash,
trimTrailingSlash,
} from 'hono/trailing-slash'
```
## 使い方
`/about/me` への GET リクエストを `/about/me/` へリダイレクトする例です。
```ts
import { Hono } from 'hono'
import { appendTrailingSlash } from 'hono/trailing-slash'
const app = new Hono({ strict: true })
app.use(appendTrailingSlash())
app.get('/about/me/', (c) => c.text('With Trailing Slash'))
```
`/about/me/` への GET リクエストを `/about/me` へリダイレクトする例です。
```ts
import { Hono } from 'hono'
import { trimTrailingSlash } from 'hono/trailing-slash'
const app = new Hono({ strict: true })
app.use(trimTrailingSlash())
app.get('/about/me', (c) => c.text('Without Trailing Slash'))
```
## オプション
### alwaysRedirect: `boolean`
デフォルトでは、 Trailing Slash ミドルウェアはレスポンスステータスが `404` の場合にのみリダイレクトします。 `alwaysRedirect` を `true` に設定すると、ミドルウェアはハンドラーを実行する前にリダイレクトします。 これは、デフォルトの挙動が機能しないワイルドカードルート (`*`) に便利です。
```ts
const app = new Hono()
app.use(trimTrailingSlash({ alwaysRedirect: true }))
app.get('/my-path/*', (c) => c.text('Wildcard route'))
```
このオプションは、 `trimTrailingSlash` と `appendTrailingSlash` の両方で利用できます。
### skip: `(path: string) => boolean`
リクエストパスに基づいて、リダイレクトをスキップするかどうかを判定する関数です。 関数が `true` を返した場合、リダイレクトはスキップされます。 これは、ファイル拡張子を持つパスなど、特定のパスをリダイレクト対象から除外したい場合に便利です。
```ts
app.use(
appendTrailingSlash({
skip: (path) => /\.\w+$/.test(path),
})
)
```
このオプションは、 `trimTrailingSlash` と `appendTrailingSlash` の両方で利用できます。
## Note
リクエストメソッドが `GET` で、レスポンスステータスが `404` の場合に有効になります。
# Accepts ヘッダ
Accepts ヘルパーはリクエストの Accept ヘッダを処理するのに役立ちます。
## インポート
```ts
import { Hono } from 'hono'
import { accepts } from 'hono/accepts'
```
## `accepts()`
`accepts()` 関数は Accept-Encoding や Accept-Language 等の Accept をヘッダ調べ、適切な値を返します。
```ts
import { accepts } from 'hono/accepts'
app.get('/', (c) => {
const accept = accepts(c, {
header: 'Accept-Language',
supports: ['en', 'ja', 'zh'],
default: 'en',
})
return c.json({ lang: accept })
})
```
### `AcceptHeader` 型
`AcceptHeader` 型の定義は以下のとおりです。
```ts
export type AcceptHeader =
| 'Accept'
| 'Accept-Charset'
| 'Accept-Encoding'
| 'Accept-Language'
| 'Accept-Patch'
| 'Accept-Post'
| 'Accept-Ranges'
```
## オプション
### header: `AcceptHeader`
調べる accept ヘッダの名前。
### supports: `string[]`
アプリケーションがサポートするヘッダの値。
### default: `string`
デフォルトの値。
### match: `(accepts: Accept[], config: acceptsConfig) => string`
マッチを行うカスタム関数。
# アダプタヘルパー
Adapter Helper は、統一されたインターフェースを通じて様々なプラットフォームとシームレスにやり取りする方法を提供します。
## Import
```ts
import { Hono } from 'hono'
import { env, getRuntimeKey } from 'hono/adapter'
```
## `env()`
`env()` 関数は、 Cloudflare Workers の Bindings にとどまらず、異なるランタイム間で環境変数を取得することを容易にします。 `env(c)` で取得できる値は、ランタイムごとに異なる場合があります。
```ts
import { env } from 'hono/adapter'
app.get('/env', (c) => {
// NAME is process.env.NAME on Node.js or Bun
// NAME is the value written in `wrangler.toml` on Cloudflare
const { NAME } = env<{ NAME: string }>(c)
return c.text(NAME)
})
```
サポートされているランタイム、サーバーレスプラットフォーム、クラウドサービス:
- Cloudflare Workers
- `wrangler.toml`
- `wrangler.jsonc`
- Deno
- [`Deno.env`](https://docs.deno.com/runtime/manual/basics/env_variables)
- `.env` ファイル
- Bun
- [`Bun.env`](https://bun.com/guides/runtime/set-env)
- `process.env`
- Node.js
- `process.env`
- Vercel
- [Environment Variables on Vercel](https://vercel.com/docs/projects/environment-variables)
- AWS Lambda
- [Environment Variables on AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/samples-blank.html#samples-blank-architecture)
- Lambda@Edge\
Lambda の環境変数は Lambda@Edge では [サポートされていません](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/add-origin-custom-headers.html) 。 代わりに [Lambda@Edge イベント](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html) を使用する必要があります。
- Fastly Compute\
Fastly Compute では、 ConfigStore を使用してユーザー定義のデータを管理できます。
- Netlify\
Netlify では、 [Netlify Contexts](https://docs.netlify.com/site-deploys/overview/#deploy-contexts) を使用してユーザー定義のデータを管理できます。
### ランタイムの指定
第2引数にランタイムキーを渡すことで、環境変数を取得するランタイムを指定できます。
```ts
app.get('/env', (c) => {
const { NAME } = env<{ NAME: string }>(c, 'workerd')
return c.text(NAME)
})
```
## `getRuntimeKey()`
`getRuntimeKey()` 関数は、現在のランタイムの識別子を返します。
```ts
app.get('/', (c) => {
if (getRuntimeKey() === 'workerd') {
return c.text('You are on Cloudflare')
} else if (getRuntimeKey() === 'bun') {
return c.text('You are on Bun')
}
...
})
```
### 利用可能なランタイムキー
以下は利用可能なランタイムキーの一覧です。 利用できないランタイムキーは、サポートされる可能性があり、 `other` というラベルが付けられます。 一部のキーは [WinterCG's Runtime Keys](https://runtime-keys.proposal.wintercg.org/) に着想を得ています:
- `workerd` - Cloudflare Workers
- `deno`
- `bun`
- `node`
- `edge-light` - Vercel Edge Functions
- `fastly` - Fastly Compute
- `other` - その他の未知のランタイムキー
# ConnInfo ヘルパー
ConnInfo ヘルパーはネットワーク接続の情報を得るために役立ちます。 例えば、クライアントのリモートアドレスを簡単に取得できます。
## インポート
The adapter for each runtime is a separate package (`@hono/cloudflare-workers`, `@hono/deno`, `@hono/bun`, ...). Install the one for your runtime.
::: code-group
```ts [Cloudflare Workers]
import { Hono } from 'hono'
import { getConnInfo } from '@hono/cloudflare-workers'
```
```ts [Deno]
import { Hono } from 'hono'
import { getConnInfo } from '@hono/deno'
```
```ts [Bun]
import { Hono } from 'hono'
import { getConnInfo } from '@hono/bun'
```
```ts [Vercel]
import { Hono } from 'hono'
import { getConnInfo } from '@hono/vercel'
```
```ts [AWS Lambda]
import { Hono } from 'hono'
import { getConnInfo } from '@hono/aws-lambda'
```
```ts [Netlify]
import { Hono } from 'hono'
import { getConnInfo } from '@hono/netlify'
```
```ts [Lambda@Edge]
import { Hono } from 'hono'
import { getConnInfo } from '@hono/lambda-edge'
```
```ts [Node.js]
import { Hono } from 'hono'
import { getConnInfo } from '@hono/node-server/conninfo'
```
:::
## 使用方法
```ts
const app = new Hono()
app.get('/', (c) => {
const info = getConnInfo(c) // info is `ConnInfo`
return c.text(`Your remote address is ${info.remote.address}`)
})
```
## 型定義
`getConnInfo()` で得られる情報の型定義は以下の通りです:
```ts
type AddressType = 'IPv6' | 'IPv4' | undefined
type NetAddrInfo = {
/**
* Transport protocol type
*/
transport?: 'tcp' | 'udp'
/**
* Transport port number
*/
port?: number
address?: string
addressType?: AddressType
} & (
| {
/**
* Host name such as IP Addr
*/
address: string
/**
* Host name type
*/
addressType: AddressType
}
| {}
)
/**
* HTTP Connection information
*/
interface ConnInfo {
/**
* Remote information
*/
remote: NetAddrInfo
}
```
# Cookie ヘルパー
Cookie Helper は、クッキーを管理するための簡単なインターフェースを提供し、開発者がクッキーの設定、パース、削除をシームレスに行えるようにします。
## Import
```ts
import { Hono } from 'hono'
import {
deleteCookie,
getCookie,
getSignedCookie,
setCookie,
setSignedCookie,
generateCookie,
generateSignedCookie,
} from 'hono/cookie'
```
## 使い方
### 通常のクッキー
```ts
app.get('/cookie', (c) => {
setCookie(c, 'cookie_name', 'cookie_value')
const yummyCookie = getCookie(c, 'cookie_name')
deleteCookie(c, 'cookie_name')
const allCookies = getCookie(c)
// ...
})
```
### 署名付きクッキー
**NOTE**: 署名付きクッキーの設定と取得は Promise を返します。 これは、 HMAC SHA-256 署名の作成に使用される WebCrypto API が非同期であるためです。
```ts
app.get('/signed-cookie', (c) => {
const secret = 'secret' // make sure it's a large enough string to be secure
await setSignedCookie(c, 'cookie_name0', 'cookie_value', secret)
const fortuneCookie = await getSignedCookie(
c,
secret,
'cookie_name0'
)
deleteCookie(c, 'cookie_name0')
// `getSignedCookie` will return `false` for a specified cookie if its signature fails verification
const allSignedCookies = await getSignedCookie(c, secret)
// ...
})
```
> [!NOTE]
> `getSignedCookie` は2つの場合を区別します。 署名はあるものの検証に失敗したクッキーは `false` を返します。 有効な署名形式を持たないクッキーは、そもそも署名付きクッキーとして扱われず、クッキーが存在しない場合と同様に `undefined` を返します。 このルールは、名前を指定して単一のクッキーを取得する場合も、すべての署名付きクッキーを取得する場合も適用されます。 `false` と `undefined` はどちらも falsy なので、 `if (!value)` で両方の場合を処理できます。
### クッキーの生成
`generateCookie` と `generateSignedCookie` 関数を使うと、レスポンスヘッダーに設定することなく、クッキー文字列を直接作成できます。
#### `generateCookie`
```ts
// Basic cookie generation
const cookie = generateCookie('delicious_cookie', 'macha')
// Returns: 'delicious_cookie=macha; Path=/'
// Cookie with options
const cookie = generateCookie('delicious_cookie', 'macha', {
path: '/',
secure: true,
httpOnly: true,
domain: 'example.com',
})
```
#### `generateSignedCookie`
```ts
// Basic signed cookie generation
const signedCookie = await generateSignedCookie(
'delicious_cookie',
'macha',
'secret chocolate chips'
)
// Signed cookie with options
const signedCookie = await generateSignedCookie(
'delicious_cookie',
'macha',
'secret chocolate chips',
{
path: '/',
secure: true,
httpOnly: true,
}
)
```
**Note**: `setCookie` や `setSignedCookie` とは異なり、これらの関数はクッキー文字列を生成するだけです。 必要に応じて、手動でヘッダーに設定する必要があります。
## オプション
### `setCookie` & `setSignedCookie`
- domain: `string`
- expires: `Date`
- httpOnly: `boolean`
- maxAge: `number`
- path: `string`
- secure: `boolean`
- sameSite: `'Strict'` | `'Lax'` | `'None'`
- priority: `'Low' | 'Medium' | 'High'`
- prefix: `secure` | `'host'`
- partitioned: `boolean`
例:
```ts
// Regular cookies
setCookie(c, 'great_cookie', 'banana', {
path: '/',
secure: true,
domain: 'example.com',
httpOnly: true,
maxAge: 1000,
expires: new Date(Date.UTC(2000, 11, 24, 10, 30, 59, 900)),
sameSite: 'Strict',
})
// Signed cookies
await setSignedCookie(
c,
'fortune_cookie',
'lots-of-money',
'secret ingredient',
{
path: '/',
secure: true,
domain: 'example.com',
httpOnly: true,
maxAge: 1000,
expires: new Date(Date.UTC(2000, 11, 24, 10, 30, 59, 900)),
sameSite: 'Strict',
}
)
```
### `deleteCookie`
- path: `string`
- secure: `boolean`
- domain: `string`
例:
```ts
deleteCookie(c, 'banana', {
path: '/',
secure: true,
domain: 'example.com',
})
```
`deleteCookie` は削除された値を返します:
```ts
const deletedCookie = deleteCookie(c, 'delicious_cookie')
```
## `__Secure-` と `__Host-` プレフィックス
Cookie Helper は、クッキー名に対する `__Secure-` と `__Host-` プレフィックスをサポートしています。
クッキー名がプレフィックスを持つか検証したい場合は、 prefix オプションを指定してください。
```ts
const securePrefixCookie = getCookie(c, 'yummy_cookie', 'secure')
const hostPrefixCookie = getCookie(c, 'yummy_cookie', 'host')
const securePrefixSignedCookie = await getSignedCookie(
c,
secret,
'fortune_cookie',
'secure'
)
const hostPrefixSignedCookie = await getSignedCookie(
c,
secret,
'fortune_cookie',
'host'
)
```
また、クッキーを設定する際にプレフィックスを指定したい場合は、 prefix オプションに値を指定してください。
```ts
setCookie(c, 'delicious_cookie', 'macha', {
prefix: 'secure', // or `host`
})
await setSignedCookie(
c,
'delicious_cookie',
'macha',
'secret choco chips',
{
prefix: 'secure', // or `host`
}
)
```
## ベストプラクティスへの準拠
新しい Cookie RFC (いわゆる cookie-bis) と CHIPS には、開発者が従うべきクッキー設定のベストプラクティスがいくつか含まれています。
- [RFC6265bis-13](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-13)
- `Max-Age`/`Expires` の制限
- `__Host-`/`__Secure-` プレフィックスの制限
- [CHIPS-01](https://www.ietf.org/archive/id/draft-cutler-httpbis-partitioned-cookies-01.html)
- `Partitioned` の制限
Hono はこれらのベストプラクティスに従っています。
Cookie Helper は、以下の条件でクッキーをパースする際に `Error` をスローします:
- クッキー名が `__Secure-` で始まるが、 `secure` オプションが設定されていない。
- クッキー名が `__Host-` で始まるが、 `secure` オプションが設定されていない。
- クッキー名が `__Host-` で始まるが、 `path` が `/` ではない。
- クッキー名が `__Host-` で始まるが、 `domain` が設定されている。
- `maxAge` オプションの値が400日より大きい。
- `expires` オプションの値が現在時刻より400日後である。
# css ヘルパー
`hono/css` CSS ヘルパーは Hono のビルトイン CSS in JS(X) ツールです。
JavaScript で `css` テンプレートリテラルを使うことで CSS in JSX を実現できます。 `css` の戻り値はクラス名で、 class 属性の値に設定できます。 `` コンポーネントには CSS のコードが含まれます。
## インポート
```ts
import { Hono } from 'hono'
import { css, cx, keyframes, Style, createCssContext } from 'hono/css'
```
## `css`
`css` テンプレートリテラル内に CSS を記述できます。 この例では、 `headerClass` を `class` 属性の値として使用します。 `` には CSS コードが含まれているので、追加することを忘れないでください。
```ts{10,13}
app.get('/', (c) => {
const headerClass = css`
background-color: orange;
color: white;
padding: 1rem;
`
return c.html(
)
})
```
`:hover` などの擬似クラスも `&` [入れ子セレクター](https://developer.mozilla.org/ja/docs/Web/CSS/Reference/Selectors/Nesting_selector) を用いてスタイリングできます:
```ts
const buttonClass = css`
background-color: #fff;
&:hover {
background-color: red;
}
`
```
### 拡張
クラス名を埋め込むことで CSS の定義を拡張できます。
```tsx
const baseClass = css`
color: white;
background-color: blue;
`
const header1Class = css`
${baseClass}
font-size: 3rem;
`
const header2Class = css`
${baseClass}
font-size: 2rem;
`
```
また、 `${baseClass} {}` 構文によってネストされたクラスが実現できます。
```tsx
const headerClass = css`
color: white;
background-color: blue;
`
const containerClass = css`
${headerClass} {
h1 {
font-size: 3rem;
}
}
`
return c.render(
)
```
### グローバルなスタイル
`:-hono-global` 疑似セレクタを用いることでグローバルなスタイルを定義できます。
```tsx
const globalClass = css`
:-hono-global {
html {
font-family: Arial, Helvetica, sans-serif;
}
}
`
return c.render(
Hello!
Today is a good day.
)
```
または、 `` コンポーネントに `css` リテラルを用いて CSS を書くこともできます。
```tsx
export const renderer = jsxRenderer(({ children, title }) => {
return (
{title}
{children}
)
})
```
## `keyframes`
`keyframes` テンプレートリテラルを用いて `@keyframes` の内容を書くことができます。 この例では、 `fadeInAnimation` がアニメーションの名前になります。
```tsx
const fadeInAnimation = keyframes`
from {
opacity: 0;
}
to {
opacity: 1;
}
`
const headerClass = css`
animation-name: ${fadeInAnimation};
animation-duration: 2s;
`
const Header = () =>
```
## `cx`
`cx` は2つのクラス名を統合します。
```tsx
const buttonClass = css`
border-radius: 10px;
`
const primaryClass = css`
background: orange;
`
const Button = () => (
Click!
)
```
また、クラス以外のシンプルなセレクタも統合できます。
```tsx
const Header = () => Hi
```
## [Secure Headers](/docs/middleware/builtin/secure-headers) ミドルウェアと組み合わせて使用する
[Secure Headers](/docs/middleware/builtin/secure-headers) ミドルウェアと CSS ヘルパーを組み合わせて使用したいとき、 [`nonce` 属性](https://developer.mozilla.org/ja/docs/Web/HTML/Reference/Global_attributes/nonce) を `` のように使用することによって CSS ヘルパーによってもたらされる Content-Security-Policy の問題を回避できます。
```tsx{8,23}
import { secureHeaders, NONCE } from 'hono/secure-headers'
app.get(
'*',
secureHeaders({
contentSecurityPolicy: {
// Set the pre-defined nonce value to `styleSrc`:
styleSrc: [NONCE],
},
})
)
app.get('/', (c) => {
const headerClass = css`
background-color: orange;
color: white;
padding: 1rem;
`
return c.html(
{/* Set the `nonce` attribute on the CSS helpers `style` and `script` elements */}
)
})
```
## `createCssContext`
`createCssContext` は、カスタムコンテキストを持つ CSS ヘルパー関数 (`css` 、 `cx` 、 `keyframes` 、 `viewTransition` 、 `Style`) を作成します。 これを使用して、 style 要素の ID や生成されるクラス名をカスタマイズできます。
```ts
import { createCssContext } from 'hono/css'
const { css, cx, keyframes, Style } = createCssContext({
id: 'my-app',
})
```
### `classNameSlug`
デフォルトでは、 CSS クラス名は `css-1234567890` の形式で生成されます。 `classNameSlug` 関数を渡すことで、これをカスタマイズできます。
この関数は3つの引数を受け取ります:
- `hash` - デフォルトで生成されたクラス名 (例: `css-1234567890`)
- `label` - CSS テンプレートの先頭にある `/* comment */` から抽出されたもの (なければ空文字列)
- `css` - ミニファイされた CSS 文字列
```ts
const { css, Style } = createCssContext({
id: 'my-styles',
classNameSlug: (hash, label) => (label ? `h-${label}` : hash),
})
const heroClass = css`
/* hero-section */
background: blue;
`
// Generated class name: "h-hero-section"
```
### `onInvalidSlug`
`classNameSlug` 関数が無効な CSS クラス名を返した場合、デフォルトでは警告がログに出力されます。 この挙動は `onInvalidSlug` でカスタマイズできます。
```ts
const { css, Style } = createCssContext({
id: 'my-styles',
classNameSlug: (hash, label) => label || hash,
onInvalidSlug: (slug) => {
throw new Error(`Invalid CSS class name: ${slug}`)
},
})
```
## セキュリティ
CSS Helper は CSS を記述するための API です。 他の CSS-in-JS ライブラリと同様に、補間された値は **生の CSS** として挿入されます。 HTML への脱出 (引用符、バックスラッシュ、 ``) はブロックされますが、 `{` 、 `}` 、 `;` は有効な CSS であるためそのまま通過します。
::: warning
CSS Helper は他の raw シンク (`html` 、 `raw` 、 `rawCssString`) と同様に扱ってください。**信頼できない入力を直接渡さないでください。** 渡すと CSS インジェクションが可能になります。 まず許可リストに対して検証してください。
```tsx
const ALLOWED_COLORS = ['red', 'green', 'blue']
const color = ALLOWED_COLORS.includes(input) ? input : 'black'
const headerClass = css`
color: ${color};
`
```
:::
## Tips
VS Code を使用している場合は、 [vscode-styled-components](https://marketplace.visualstudio.com/items?itemName=styled-components.vscode-styled-components) を使用することによって CSS タグ付きリテラルにシンタックスハイライトと IntelliSense が提供されます。

# Dev ヘルパー
Dev ヘルパーは開発中に使用することができる便利なメソッドを提供します。
```ts
import { Hono } from 'hono'
import { getRouterName, showRoutes } from 'hono/dev'
```
## `getRouterName()`
現在使用しているルーターの名前を `getRouterName()` で取得できます。
```ts
const app = new Hono()
// ...
console.log(getRouterName(app))
```
## `showRoutes()`
`showRoutes()` 関数はコンソールに登録されているルートを表示します。
次のようなアプリケーションを考えてみましょう:
```ts
const app = new Hono().basePath('/v1')
app.get('/posts', (c) => {
// ...
})
app.get('/posts/:id', (c) => {
// ...
})
app.post('/posts', (c) => {
// ...
})
showRoutes(app, {
verbose: true,
})
```
アプリケーションが起動する時、このようにルートがコンソールに表示されます:
```txt
GET /v1/posts
GET /v1/posts/:id
POST /v1/posts
```
## オプション
### verbose: `boolean`
`true` に設定すると、詳細な情報を表示します。
### colorize: `boolean`
`false` に設定すると、出力に色が付きません。
# Factory ヘルパー
Factory ヘルパーはミドルウェアのような Hono のコンポーネントを作るときに有用な機能を提供します。 時々、適切な TypeScript の型を設定することが難しい場合もありますが、このヘルパーはそれを簡単にします。
## インポート
```ts
import { Hono } from 'hono'
import { createFactory, createMiddleware } from 'hono/factory'
```
## `createFactory()`
`createFactory()` は Factory クラスのインスタンスを作ります。
```ts
import { createFactory } from 'hono/factory'
const factory = createFactory()
```
`Env` (バインディングや `c.var.*` など) の型をジェネリクスとして渡すことが出来ます:
```ts
type Env = {
Variables: {
foo: string
}
}
const factory = createFactory()
```
### Options
### defaultAppOptions: `HonoOptions`
`createApp()` で作成される Hono アプリケーションに渡されるデフォルトのオプションです。
```ts
const factory = createFactory({
defaultAppOptions: { strict: false },
})
const app = factory.createApp() // `strict: false` is applied
```
## `createMiddleware()`
`createMiddleware()` は `factory.createMiddleware()` のショートカットです。
この関数は、カスタムミドルウェアを作成します。
```ts
const messageMiddleware = createMiddleware(async (c, next) => {
await next()
c.res.headers.set('X-Message', 'Good morning!')
})
```
ヒント: `message` のような引数を受け取りたい場合、次のような関数を作ることが出来ます。
```ts
const messageMiddleware = (message: string) => {
return createMiddleware(async (c, next) => {
await next()
c.res.headers.set('X-Message', message)
})
}
app.use(messageMiddleware('Good evening!'))
```
## `factory.createHandlers()`
`createHandlers()` は `app.get('/')` とは別の場所でハンドラを定義するのに役立ちます。
```ts
import { createFactory } from 'hono/factory'
import { logger } from 'hono/logger'
// ...
const factory = createFactory()
const middleware = factory.createMiddleware(async (c, next) => {
c.set('foo', 'bar')
await next()
})
const handlers = factory.createHandlers(logger(), middleware, (c) => {
return c.json(c.var.foo)
})
app.get('/api', ...handlers)
```
## `factory.createApp()`
`createApp()` は適切な型で Hono のインスタンスを作ることを助けてくれます。 このメソッドを `createFactory()` と一緒に使うと、 `Env` の型定義の重複を避けることが出来ます。
次のようなアプリケーションは、 `Env` を二箇所で設定する必要があります:
```ts
import { createMiddleware } from 'hono/factory'
type Env = {
Variables: {
myVar: string
}
}
// 1. Set the `Env` to `new Hono()`
const app = new Hono()
// 2. Set the `Env` to `createMiddleware()`
const mw = createMiddleware(async (c, next) => {
await next()
})
app.use(mw)
```
`createFactory()` と `createApp()` を使うことで、 `Env` を一箇所だけセットするだけで良くなります。
```ts
import { createFactory } from 'hono/factory'
// ...
// Set the `Env` to `createFactory()`
const factory = createFactory()
const app = factory.createApp()
// factory also has `createMiddleware()`
const mw = factory.createMiddleware(async (c, next) => {
await next()
})
```
`createFactory()` には `createApp()` で作られた `app` を初期化するための `initApp` オプションを渡すことが出来ます。 このようにオプションを使います。
```ts
// factory-with-db.ts
type Env = {
Bindings: {
MY_DB: D1Database
}
Variables: {
db: DrizzleD1Database
}
}
export default createFactory({
initApp: (app) => {
app.use(async (c, next) => {
const db = drizzle(c.env.MY_DB)
c.set('db', db)
await next()
})
},
})
```
```ts
// crud.ts
import factoryWithDB from './factory-with-db'
const app = factoryWithDB.createApp()
app.post('/posts', (c) => {
c.var.db.insert()
// ...
})
```
# HTML ヘルパー
HTML Helper を使うと、 `html` というタグ付きテンプレートで JavaScript のテンプレートリテラル内に HTML を書けます。 `raw()` を使うと、コンテンツはそのままレンダリングされます。 これらの文字列は自分でエスケープする必要があります。
## Import
```ts
import { Hono } from 'hono'
import { html, raw } from 'hono/html'
```
## `html`
```ts
const app = new Hono()
app.get('/:username', (c) => {
const { username } = c.req.param()
return c.html(
html`
Hello! ${username}!
`
)
})
```
### JSX へのスニペット挿入
インラインスクリプトを JSX に挿入します:
```tsx
app.get('/', (c) => {
return c.html(
Test Site
{html`
`}
Hello!
)
})
```
### 関数コンポーネントとして動作
`html` は HtmlEscapedString を返すので、 JSX を使わずとも完全に機能するコンポーネントとして動作できます。
#### `memo` の代わりに `html` を使って処理を高速化する
```typescript
const Footer = () => html`
`
```
### props の受け取りと値の埋め込み
```typescript
interface SiteData {
title: string
description: string
image: string
children?: any
}
const Layout = (props: SiteData) => html`
${props.title}
${props.children}
`
const Content = (props: { siteData: SiteData; name: string }) => (
Hello {props.name}
)
app.get('/', (c) => {
const props = {
name: 'World',
siteData: {
title: 'Hello <> World',
description: 'This is a description',
image: 'https://example.com/image.png',
},
}
return c.html()
})
```
## `raw()`
```ts
app.get('/', (c) => {
const name = 'John "Johnny" Smith'
return c.html(html`I'm ${raw(name)}.
`)
})
```
## Tips
これらのライブラリのおかげで、 Visual Studio Code や vim もテンプレートリテラルを HTML として解釈し、シンタックスハイライトやフォーマットを適用できます。
-
-
# JWT 認証ヘルパー
このヘルパーは、 JSON Web Token (JWT) のエンコード、デコード、署名、検証のための関数を提供します。 JWT は、 Web アプリケーションで認証や認可の目的で広く使われています。 このヘルパーは、様々な暗号化アルゴリズムをサポートする堅牢な JWT 機能を提供します。
## Import
このヘルパーを使用するには、次のようにインポートします:
```ts
import { decode, sign, verify } from 'hono/jwt'
```
::: info
[JWT ミドルウェア](/docs/middleware/builtin/jwt) も `hono/jwt` から `jwt` 関数をインポートします。
:::
## `sign()`
この関数は、ペイロードをエンコードし、指定されたアルゴリズムとシークレットで署名することで、 JWT トークンを生成します。
```ts
sign(
payload: unknown,
secret: string,
alg?: 'HS256';
): Promise;
```
### 例
```ts
import { sign } from 'hono/jwt'
const payload = {
sub: 'user123',
role: 'admin',
exp: Math.floor(Date.now() / 1000) + 60 * 5, // Token expires in 5 minutes
}
const secret = 'mySecretKey'
const token = await sign(payload, secret)
```
### オプション
#### payload: `unknown`
署名される JWT のペイロードです。 [ペイロードの検証](#payload-validation) にあるような他のクレームを含めることができます。
#### secret: `string`
JWT の検証や署名に使用されるシークレットキーです。
#### alg: [AlgorithmTypes](#supported-algorithmtypes)
JWT の署名や検証に使用されるアルゴリズムです。 デフォルトは HS256 です。
## `verify()`
この関数は、 JWT トークンが本物でまだ有効であるかを確認します。 トークンが改ざんされていないことを保証し、 [ペイロードの検証](#payload-validation) を追加した場合にのみ有効性をチェックします。
```ts
verify(
token: string,
secret: string,
alg: 'HS256';
issuer?: string | RegExp;
aud?: string | string[] | RegExp;
): Promise;
```
### 例
```ts
import { verify } from 'hono/jwt'
const tokenToVerify = 'token'
const secretKey = 'mySecretKey'
const decodedPayload = await verify(tokenToVerify, secretKey, 'HS256')
console.log(decodedPayload)
```
### オプション
#### token: `string`
検証対象の JWT トークンです。
#### secret: `string`
JWT の検証や署名に使用されるシークレットキーです。
#### alg: [AlgorithmTypes](#supported-algorithmtypes)
JWT の署名や検証に使用されるアルゴリズムです。
#### issuer: `string | RegExp`
JWT の検証に使用される期待される issuer です。
#### aud: `string | string[] | RegExp`
JWT の検証に使用される期待されるオーディエンスです。 これが設定されている場合、トークンは `aud` クレームを含んでいなければならず、少なくとも1つのオーディエンス値が一致する必要があります。
## `decode()`
この関数は、署名の検証を行わずに JWT トークンをデコードします。 トークンからヘッダーとペイロードを抽出して返します。
```ts
decode(token: string): { header: any; payload: any };
```
### 例
```ts
import { decode } from 'hono/jwt'
// Decode the JWT token
const tokenToDecode =
'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAidXNlcjEyMyIsICJyb2xlIjogImFkbWluIn0.JxUwx6Ua1B0D1B0FtCrj72ok5cm1Pkmr_hL82sd7ELA'
const { header, payload } = decode(tokenToDecode)
console.log('Decoded Header:', header)
console.log('Decoded Payload:', payload)
```
### オプション
#### token: `string`
デコード対象の JWT トークンです。
> `decode` 関数を使うと、検証を行わ _**ずに**_ JWT トークンのヘッダーとペイロードを調査できます。 これは、デバッグや JWT トークンからの情報抽出に役立ちます。
## ペイロードの検証
JWT トークンの検証時には、以下のペイロード検証が行われます:
- `exp`: トークンの有効期限が切れていないかチェックされます。
- `nbf`: トークンが指定された時刻より前に使用されていないかチェックされます。
- `iat`: トークンが未来に発行されていないかチェックされます。
- `iss`: トークンが信頼できる issuer によって発行されたものかチェックされます。
- `aud`: `aud` 検証パラメータが設定されている場合、トークンが許可されたオーディエンス向けのものであるかチェックされます。
検証時にこれらのチェックを行いたい場合は、 JWT ペイロードにこれらのフィールドをオブジェクトとして含めてください。
## カスタムエラー型
このモジュールは、 JWT 関連のエラーを処理するためのカスタムエラー型も定義しています。
- `JwtAlgorithmNotImplemented`: リクエストされた JWT アルゴリズムが実装されていないことを示します。
- `JwtTokenInvalid`: JWT トークンが無効であることを示します。
- `JwtTokenNotBefore`: トークンが有効日より前に使用されていることを示します。
- `JwtTokenExpired`: トークンの有効期限が切れていることを示します。
- `JwtTokenIssuedAt`: トークン内の "iat" クレームが正しくないことを示します。
- `JwtTokenIssuer`: トークン内の "iss" クレームが正しくないことを示します。
- `JwtPayloadRequiresAud`: `aud` 検証が設定されている場合に `aud` クレームが必要であることを示します。
- `JwtTokenAudience`: トークンの `aud` クレームが期待されるオーディエンスと一致しないことを示します。
- `JwtTokenSignatureMismatched`: トークン内の署名の不一致を示します。
## サポートされている AlgorithmTypes
このモジュールは、以下の JWT 暗号化アルゴリズムをサポートしています:
- `HS256`: HMAC using SHA-256
- `HS384`: HMAC using SHA-384
- `HS512`: HMAC using SHA-512
- `RS256`: RSASSA-PKCS1-v1_5 using SHA-256
- `RS384`: RSASSA-PKCS1-v1_5 using SHA-384
- `RS512`: RSASSA-PKCS1-v1_5 using SHA-512
- `PS256`: RSASSA-PSS using SHA-256 and MGF1 with SHA-256
- `PS384`: RSASSA-PSS using SHA-386 and MGF1 with SHA-386
- `PS512`: RSASSA-PSS using SHA-512 and MGF1 with SHA-512
- `ES256`: ECDSA using P-256 and SHA-256
- `ES384`: ECDSA using P-384 and SHA-384
- `ES512`: ECDSA using P-521 and SHA-512
- `EdDSA`: EdDSA using Ed25519
# プロキシヘルパー
Proxy Helper は、 Hono アプリケーションを (リバース) プロキシとして使用する際に役立つ関数を提供します。
## Import
```ts
import { Hono } from 'hono'
import { proxy } from 'hono/proxy'
```
## `proxy()`
`proxy()` は、プロキシのための `fetch()` API ラッパーです。 引数と戻り値は `fetch()` と同じです (プロキシ固有のオプションを除く)。
`Accept-Encoding` ヘッダーは、現在のランタイムが処理できるエンコーディングに置き換えられます。 不要なレスポンスヘッダーは削除され、ハンドラーから送信できる `Response` オブジェクトが返されます。
### 例
シンプルな使い方:
```ts
app.get('/proxy/:path', (c) => {
return proxy(`http://${originServer}/${c.req.param('path')}`)
})
```
複雑な使い方:
```ts
app.get('/proxy/:path', async (c) => {
const res = await proxy(
`http://${originServer}/${c.req.param('path')}`,
{
headers: {
...c.req.header(), // optional, specify only when forwarding all the request data (including credentials) is necessary.
'X-Forwarded-For': '127.0.0.1',
'X-Forwarded-Host': c.req.header('host'),
Authorization: undefined, // do not propagate request headers contained in c.req.header('Authorization')
},
}
)
res.headers.delete('Set-Cookie')
return res
})
```
または、 `c.req` を引数として渡すこともできます。
```ts
app.all('/proxy/:path', (c) => {
return proxy(`http://${originServer}/${c.req.param('path')}`, {
...c.req, // optional, specify only when forwarding all the request data (including credentials) is necessary.
headers: {
...c.req.header(),
'X-Forwarded-For': '127.0.0.1',
'X-Forwarded-Host': c.req.header('host'),
Authorization: undefined, // do not propagate request headers contained in c.req.header('Authorization')
},
})
})
```
`customFetch` オプションで、デフォルトのグローバル `fetch` 関数をオーバーライドできます:
```ts
app.get('/proxy', (c) => {
return proxy('https://example.com/', {
customFetch,
})
})
```
### Connection ヘッダーの処理
デフォルトでは、 `proxy()` は Hop-by-Hop ヘッダーインジェクション攻撃を防ぐために `Connection` ヘッダーを無視します。 `strictConnectionProcessing` オプションで、厳密な RFC 9110 準拠を有効にできます:
```ts
// Default behavior (recommended for untrusted clients)
app.get('/proxy/:path', (c) => {
return proxy(`http://${originServer}/${c.req.param('path')}`, c.req)
})
// Strict RFC 9110 compliance (use only in trusted environments)
app.get('/internal-proxy/:path', (c) => {
return proxy(`http://${internalServer}/${c.req.param('path')}`, {
...c.req,
strictConnectionProcessing: true,
})
})
```
### `ProxyFetch`
`proxy()` の型は `ProxyFetch` として定義されており、以下の通りです
```ts
interface ProxyRequestInit extends Omit {
raw?: Request
customFetch?: (request: Request) => Promise
strictConnectionProcessing?: boolean
headers?:
| HeadersInit
| [string, string][]
| Record
| Record
}
interface ProxyFetch {
(
input: string | URL | Request,
init?: ProxyRequestInit
): Promise
}
```
# ルートヘルパー
Route Helper は、デバッグやミドルウェア開発のための拡張されたルーティング情報を提供します。 マッチしたルートや現在処理中のルートに関する詳細な情報にアクセスできます。
## インポート
```ts
import { Hono } from 'hono'
import {
matchedRoutes,
routePath,
baseRoutePath,
basePath,
} from 'hono/route'
```
## 使い方
### 基本的なルート情報
```ts
const app = new Hono()
app.get('/posts/:id', (c) => {
const currentPath = routePath(c) // '/posts/:id'
const routes = matchedRoutes(c) // Array of matched routes
return c.json({
path: currentPath,
totalRoutes: routes.length,
})
})
```
### サブアプリケーションでの使用
```ts
const app = new Hono()
const apiApp = new Hono()
apiApp.get('/posts/:id', (c) => {
return c.json({
routePath: routePath(c), // '/posts/:id'
baseRoutePath: baseRoutePath(c), // '/api'
basePath: basePath(c), // '/api' (with actual params)
})
})
app.route('/api', apiApp)
```
## `matchedRoutes()`
ミドルウェアを含む、現在のリクエストにマッチしたすべてのルートの配列を返します。
```ts
app.all('/api/*', (c, next) => {
console.log('API middleware')
return next()
})
app.get('/api/users/:id', (c) => {
const routes = matchedRoutes(c)
// Returns: [
// { method: 'ALL', path: '/api/*', handler: [Function] },
// { method: 'GET', path: '/api/users/:id', handler: [Function] }
// ]
return c.json({ routes: routes.length })
})
```
## `routePath()`
現在のハンドラーに登録されたルートのパスパターンを返します。
```ts
app.get('/posts/:id', (c) => {
console.log(routePath(c)) // '/posts/:id'
return c.text('Post details')
})
```
### インデックスパラメータの使用
`Array.prototype.at()` と同様に、インデックスパラメータを渡すことで特定の位置のルートパスを取得できます。
```ts
app.all('/api/*', (c, next) => {
return next()
})
app.get('/api/users/:id', (c) => {
console.log(routePath(c, 0)) // '/api/*' (first matched route)
console.log(routePath(c, -1)) // '/api/users/:id' (last matched route)
return c.text('User details')
})
```
## `baseRoutePath()`
ルーティングで指定された現在のルートのベースパスパターンを返します。
```ts
const subApp = new Hono()
subApp.get('/posts/:id', (c) => {
return c.text(baseRoutePath(c)) // '/:sub'
})
app.route('/:sub', subApp)
```
### インデックスパラメータの使用
`Array.prototype.at()` と同様に、インデックスパラメータを渡すことで特定の位置の
ベースルートパスを取得できます。
```ts
app.all('/api/*', (c, next) => {
return next()
})
const subApp = new Hono()
subApp.get('/users/:id', (c) => {
console.log(baseRoutePath(c, 0)) // '/' (first matched route)
console.log(baseRoutePath(c, -1)) // '/api' (last matched route)
return c.text('User details')
})
app.route('/api', subApp)
```
## `basePath()`
実際のリクエストから埋め込まれたパラメータを含むベースパスを返します。
```ts
const subApp = new Hono()
subApp.get('/posts/:id', (c) => {
return c.text(basePath(c)) // '/api' (for request to '/api/posts/123')
})
app.route('/:sub', subApp)
```
# SSG ヘルパー
SSG ヘルパーは Hono アプリケーションから静的サイトを作成します。 登録されたルートのコンテンツを取得し、静的なファイルとして保存します。
## 使い方
### 手動
このようなシンプルな Hono のアプリケーションがある時:
```tsx
// index.tsx
const app = new Hono()
app.get('/', (c) => c.html('Hello, World!'))
app.use('/about', async (c, next) => {
c.setRenderer((content) => {
return c.html(
{content}
)
})
await next()
})
app.get('/about', (c) => {
return c.render(
<>
Hono SSG PageHello!
>
)
})
export default app
```
Node.js では、このようなビルドスクリプトを書きます:
```ts
// build.ts
import app from './index'
import { toSSG } from 'hono/ssg'
import fs from 'fs/promises'
toSSG(app, fs)
```
スクリプトを実行して、ファイルが出力されます:
```bash
ls ./static
about.html index.html
```
### Vite プラグイン
Vite プラグインである `@hono/vite-ssg` を使うと、簡単に処理を行うことができます。
詳しくは、以下のページを御覧ください:
https://github.com/honojs/vite-plugins/tree/main/packages/ssg
## toSSG
`toSSG` は静的サイトを作成するために使うメイン関数で、アプリケーションとファイルシステムモジュールを引数として受け取ります。 詳しくはこのようなものです:
### 入力
toSSG の引数は ToSSGInterface です。
```ts
export interface ToSSGInterface {
(
app: Hono,
fsModule: FileSystemModule,
options?: ToSSGOptions
): Promise
}
```
- `app` には、ルートを登録した `new Hono()` を指定します。
- `fs` には、 `node:fs/promise` のようなオブジェクトを指定します。
```ts
export interface FileSystemModule {
writeFile(path: string, data: string | Uint8Array): Promise
mkdir(
path: string,
options: { recursive: boolean }
): Promise
}
```
### Deno や Bun でアダプタを使用する
Deno や Bun で SSG を行いたい場合、 `@hono/deno` と `@hono/bun` パッケージから `toSSG` 関数が提供されています。
Deno:
```ts
import { toSSG } from '@hono/deno'
toSSG(app) // The second argument is an option typed `ToSSGOptions`.
```
Bun:
```ts
import { toSSG } from '@hono/bun'
toSSG(app) // The second argument is an option typed `ToSSGOptions`.
```
### オプション
オプションは ToSSGOptions インターフェースで指定されます。
```ts
export interface ToSSGOptions {
dir?: string
concurrency?: number
extensionMap?: Record
plugins?: SSGPlugin[]
}
```
- `dir` は静的サイトの出力先です。 デフォルトは `./static` です。
- `concurrency` は同時に処理・出力されるファイルの数です。 デフォルトは `2` です。
- `extensionMap` は `Content-Type` を key に、拡張子の文字列を value に持つオブジェクトです。 出力するファイルの拡張子を決めるために使われます。
- `plugins` は静的サイトジェネレータの機能を拡張する SSG プラグインの配列です。
### 出力
`toSSG` はこのような型の戻り値を返します。
```ts
export interface ToSSGResult {
success: boolean
files: string[]
error?: Error
}
```
## ファイルを作成する
### ルートとファイル名
このようなルールがルートと作成されるファイル名に適応されます。 デフォルトの出力ディレクトリの `./static` ではこのようになります:
- `/` -> `./static/index.html`
- `/path` -> `./static/path.html`
- `/path/` -> `./static/path/index.html`
### 拡張子
拡張子はそれぞれのルートが返す `Content-Type` に依存します。 例えば、 `c.html` で返されたレスポンスは `.html` として保存されます。
拡張子をカスタマイズしたい場合は、 `extensionMap` オプションを指定してください。
```ts
import { toSSG, defaultExtensionMap } from 'hono/ssg'
// Save `application/x-html` content with `.html`
toSSG(app, fs, {
extensionMap: {
'application/x-html': 'html',
...defaultExtensionMap,
},
})
```
スラッシュで終わるパスはファイルタイプに関係なく index.拡張子 として保存されることに注意してください。
```ts
// save to ./static/html/index.html
app.get('/html/', (c) => c.html('html'))
// save to ./static/text/index.txt
app.get('/text/', (c) => c.text('text'))
```
## ミドルウェア
これから紹介するビルトインミドルウェアは SSG の処理を補助します。
### ssgParams
Next.js の `generateStaticParams` のようなことをしたい場合に使います。
例:
```ts
app.get(
'/shops/:id',
ssgParams(async () => {
const shops = await getShops()
return shops.map((shop) => ({ id: shop.id }))
}),
async (c) => {
const shop = await getShop(c.req.param('id'))
if (!shop) {
return c.notFound()
}
return c.render(
{shop.name}
)
}
)
```
### isSSGContext
`isSSGContext` は、現在のアプリケーションが `toSSG` によってトリガーされた SSG コンテキスト内で実行されている場合に `true` を返すヘルパー関数です。
```ts
app.get('/page', (c) => {
if (isSSGContext(c)) {
return c.text('This is generated by SSG')
}
return c.text('This is served dynamically')
})
```
### disableSSG
`disableSSG` ミドルウェアを指定されたルートは `toSSG` のファイル生成から除外されます。
```ts
app.get('/api', disableSSG(), (c) => c.text('an-api'))
```
### onlySSG
`onlySSG` ミドルウェアを指定されたルートは `toSSG` 後の `c.notFound()` でオーバーライドされます。
```ts
app.get('/static-page', onlySSG(), (c) => c.html(Welcome to my site
))
```
## プラグイン
プラグインを使用すると、静的サイト生成プロセスの機能を拡張できます。 プラグインはフックを使用して、異なる段階で生成プロセスをカスタマイズします。
### デフォルトプラグイン
デフォルトでは、 `toSSG` は 200 以外のステータスレスポンス (リダイレクト、エラー、404 など) をスキップする `defaultPlugin` を使用します。 これにより、成功しなかったレスポンスのファイル生成が防止されます。
```ts
import { toSSG, defaultPlugin } from 'hono/ssg'
// defaultPlugin is automatically applied when no plugins specified
toSSG(app, fs)
// Equivalent to:
toSSG(app, fs, { plugins: [defaultPlugin] })
```
カスタムプラグインを指定した場合、 `defaultPlugin` は自動的には含まれません。 デフォルトの挙動を維持しながらカスタムプラグインを追加するには、明示的に含めてください:
```ts
toSSG(app, fs, {
plugins: [defaultPlugin, myCustomPlugin],
})
```
### リダイレクトプラグイン
`redirectPlugin` は、 HTTP リダイレクトレスポンス (301 、 302 、 303 、 307 、 308) を返すルートに対して、 HTML リダイレクトページを生成します。 生成される HTML には `` タグと canonical リンクが含まれます。
```ts
import { toSSG, redirectPlugin, defaultPlugin } from 'hono/ssg'
toSSG(app, fs, {
plugins: [redirectPlugin(), defaultPlugin()],
})
```
例えば、アプリに次のルートがある場合:
```ts
app.get('/old', (c) => c.redirect('/new'))
```
`redirectPlugin` は、 `/new` への meta refresh リダイレクトを含む HTML ファイルを `/old.html` として生成します。
> [!NOTE]
> `defaultPlugin` と併用する場合は、 `redirectPlugin` を `defaultPlugin` の**前に**配置してください。 `defaultPlugin` は 200 以外のレスポンスをスキップするため、先に配置すると `redirectPlugin` がリダイレクトレスポンスを処理できなくなります。
### フックの型
プラグインは、次のフックを使用して `toSSG` プロセスをカスタマイズできます:
```ts
export type BeforeRequestHook = (req: Request) => Request | false
export type AfterResponseHook = (res: Response) => Response | false
export type AfterGenerateHook = (
result: ToSSGResult
) => void | Promise
```
- **BeforeRequestHook**: 各リクエストの処理前に呼び出されます。 `false` を返すとそのルートをスキップします。
- **AfterResponseHook**: 各レスポンスの受信後に呼び出されます。 `false` を返すとファイル生成をスキップします。
- **AfterGenerateHook**: 生成プロセス全体の完了後に呼び出されます。
### プラグインインターフェース
```ts
export interface SSGPlugin {
beforeRequestHook?: BeforeRequestHook | BeforeRequestHook[]
afterResponseHook?: AfterResponseHook | AfterResponseHook[]
afterGenerateHook?: AfterGenerateHook | AfterGenerateHook[]
}
```
### 基本的なプラグインの例
GET リクエストのみをフィルタする場合:
```ts
const getOnlyPlugin: SSGPlugin = {
beforeRequestHook: (req) => {
if (req.method === 'GET') {
return req
}
return false
},
}
```
ステータスコードでフィルタする場合:
```ts
const statusFilterPlugin: SSGPlugin = {
afterResponseHook: (res) => {
if (res.status === 200 || res.status === 500) {
return res
}
return false
},
}
```
生成されたファイルをログに出力する場合:
```ts
const logFilesPlugin: SSGPlugin = {
afterGenerateHook: (result) => {
if (result.files) {
result.files.forEach((file) => console.log(file))
}
},
}
```
### 高度なプラグインの例
`sitemap.xml` ファイルを生成するサイトマッププラグインを作成する例です:
```ts
// plugins.ts
import fs from 'node:fs/promises'
import path from 'node:path'
import type { SSGPlugin } from 'hono/ssg'
import { DEFAULT_OUTPUT_DIR } from 'hono/ssg'
export const sitemapPlugin = (baseURL: string): SSGPlugin => {
return {
afterGenerateHook: (result, fsModule, options) => {
const outputDir = options?.dir ?? DEFAULT_OUTPUT_DIR
const filePath = path.join(outputDir, 'sitemap.xml')
const urls = result.files.map((file) =>
new URL(file, baseURL).toString()
)
const siteMapText = `
${urls.map((url) => `${url}`).join('\n')}
`
fsModule.writeFile(filePath, siteMapText)
},
}
}
```
プラグインの適用:
```ts
import app from './index'
import { toSSG } from 'hono/ssg'
import { sitemapPlugin } from './plugins'
toSSG(app, fs, {
plugins: [
getOnlyPlugin,
statusFilterPlugin,
logFilesPlugin,
sitemapPlugin('https://example.com'),
],
})
```
# Streaming ヘルパー
Streaming ヘルパーは、 SSE (Server-Sent Events) などのストリーミングレスポンスのためのメソッドを提供します。
## Import
```ts
import { Hono } from 'hono'
import { stream, streamText, streamSSE } from 'hono/streaming'
```
## `stream()`
`stream()` は簡素なストリーミングレスポンスを `Response` オブジェクトとして返します。
```ts
app.get('/stream', (c) => {
return stream(c, async (stream) => {
// Write a process to be executed when aborted.
stream.onAbort(() => {
console.log('Aborted!')
})
// Write a Uint8Array.
await stream.write(new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]))
// Pipe a readable stream.
await stream.pipe(anotherReadableStream)
})
})
```
## `streamText()`
`Content-Type: text/plain` 、 `Transfer-Encoding: chunked` 、 `X-Content-Type-Options: nosniff` をヘッダーにもつストリーミングレスポンスを返します。
```ts
app.get('/streamText', (c) => {
return streamText(c, async (stream) => {
// Write a text with a new line ('\n').
await stream.writeln('Hello')
// Wait 1 second.
await stream.sleep(1000)
// Write a text without a new line.
await stream.write(`Hono!`)
})
})
```
::: warning
Cloudflare Workers 向けのアプリケーションを開発している場合、ストリーミングは Wrangler 上ではうまく動作しないことがあります。 その場合は、 `Content-Encoding` ヘッダーに `Identity` を追加してください。
```ts
app.get('/streamText', (c) => {
c.header('Content-Encoding', 'Identity')
return streamText(c, async (stream) => {
// ...
})
})
```
:::
## `streamSSE()`
サーバー送信イベント (SSE: Server-Sent Events) をシームレスにストリーミングできます。
```ts
const app = new Hono()
let id = 0
app.get('/sse', async (c) => {
return streamSSE(c, async (stream) => {
while (!stream.aborted) {
const message = `It is ${new Date().toISOString()}`
await stream.writeSSE({
data: message,
event: 'time-update',
id: String(id++),
})
await stream.sleep(1000)
}
})
})
```
## Error Handling
Streaming ヘルパーの第3引数はエラー・ハンドラです。
この引数はオプションで、指定しなければエラーはコンソールエラーとして出力されます。
```ts
app.get('/stream', (c) => {
return stream(
c,
async (stream) => {
// Write a process to be executed when aborted.
stream.onAbort(() => {
console.log('Aborted!')
})
// Write a Uint8Array.
await stream.write(
new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f])
)
// Pipe a readable stream.
await stream.pipe(anotherReadableStream)
},
(err, stream) => {
stream.writeln('An error occurred!')
console.error(err)
}
)
})
```
コールバックが実行された後、ストリームは自動的に閉じられます。
::: warning
Streaming ヘルパーのコールバック関数内でエラーが発生した場合、 `onError` イベントは発火しません。
`onError` はレスポンスが送信される前にエラーをハンドルし、レスポンスを上書きするためのフックです。 しかしながら、コールバック関数が実行された時点でストリームは既に開始されているので、上書きすることはできません。
:::
# テストヘルパー
Testing Helper は、 Hono アプリケーションのテストを容易にするための関数を提供します。
## Import
```ts
import { Hono } from 'hono'
import { testClient } from 'hono/testing'
```
## `testClient()`
`testClient()` 関数は、 Hono のインスタンスを第1引数に取り、 [Hono Client](/docs/guides/rpc#client) と同様に、 Hono アプリケーションのルートに応じた型が付けられたオブジェクトを返します。 これにより、テスト内でエディタの自動補完を使いながら、型安全に定義したルートを呼び出せます。
**型推論に関する重要な注意:**
`testClient` がルートの型を正しく推論し、自動補完を提供するためには、 **`Hono` インスタンスに対して直接メソッドチェーンでルートを定義する必要があります**。
型推論は、チェーンされた `.get()` 、 `.post()` などの呼び出しを通じて型が流れることに依存しています。 Hono インスタンスを作成した後にルートを個別に定義した場合 ("Hello World" の例でよく見られる `const app = new Hono(); app.get(...)` のようなパターン)、 `testClient` は特定のルートに必要な型情報を持たず、型安全なクライアントの機能を利用できません。
**例:**
この例では、 `.get()` メソッドが `new Hono()` の呼び出しに直接チェーンされているため動作します:
```ts
// index.ts
const app = new Hono().get('/search', (c) => {
const query = c.req.query('q')
return c.json({ query: query, results: ['result1', 'result2'] })
})
export default app
```
```ts
// index.test.ts
import { Hono } from 'hono'
import { testClient } from 'hono/testing'
import { describe, it, expect } from 'vitest' // Or your preferred test runner
import app from './app'
describe('Search Endpoint', () => {
// Create the test client from the app instance
const client = testClient(app)
it('should return search results', async () => {
// Call the endpoint using the typed client
// Notice the type safety for query parameters (if defined in the route)
// and the direct access via .$get()
const res = await client.search.$get({
query: { q: 'hono' },
})
// Assertions
expect(res.status).toBe(200)
expect(await res.json()).toEqual({
query: 'hono',
results: ['result1', 'result2'],
})
})
})
```
テストにヘッダーを含めたい場合は、呼び出しの第2パラメータとして渡します。 第2パラメータは `RequestInit` オブジェクトとして `init` プロパティも受け取ることができ、ヘッダー、メソッド、ボディなどを設定できます。 `init` プロパティの詳細は [こちら](/docs/guides/rpc#init-option) をご覧ください。
```ts
// index.test.ts
import { Hono } from 'hono'
import { testClient } from 'hono/testing'
import { describe, it, expect } from 'vitest' // Or your preferred test runner
import app from './app'
describe('Search Endpoint', () => {
// Create the test client from the app instance
const client = testClient(app)
it('should return search results', async () => {
// Include the token in the headers and set the content type
const token = 'this-is-a-very-clean-token'
const res = await client.search.$get(
{
query: { q: 'hono' },
},
{
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': `application/json`,
},
}
)
// Assertions
expect(res.status).toBe(200)
expect(await res.json()).toEqual({
query: 'hono',
results: ['result1', 'result2'],
})
})
})
```
# WebSocket ヘルパー
WebSocket ヘルパーは、 Hono アプリケーションのサーバーサイド WebSocket のヘルパーです。
現在、 Cloudflare Workers / Pages 、 Deno 、 Bun および Node.js アダプタが利用可能です。
## Import
The adapter for each runtime is a separate package (`@hono/cloudflare-workers`, `@hono/deno`, `@hono/bun`, ...). Install the one for your runtime.
::: code-group
```ts [Cloudflare Workers]
import { Hono } from 'hono'
import { upgradeWebSocket } from '@hono/cloudflare-workers'
```
```ts [Deno]
import { Hono } from 'hono'
import { upgradeWebSocket } from '@hono/deno'
```
```ts [Bun]
import { Hono } from 'hono'
import { upgradeWebSocket, websocket } from '@hono/bun'
// ...
export default {
fetch: app.fetch,
websocket,
}
```
```ts [Node.js]
import { serve, upgradeWebSocket } from '@hono/node-server'
import { Hono } from 'hono'
import { WebSocketServer } from 'ws'
```
:::
Node.js では、 WebSocket サポートは `@hono/node-server` に組み込まれています。 有効にするには、 `ws` と、 TypeScript を使用している場合は `@types/ws` をインストールしてください。 次に、 `{ noServer: true }` を指定して `WebSocketServer` を作成し、 `websocket` オプション経由で `serve()` に渡します。
`@hono/node-ws` は非推奨です。
## `upgradeWebSocket()`
`upgradeWebSocket()` は WebSocket をハンドルするためのハンドラを返します。
```ts
const app = new Hono()
app.get(
'/ws',
upgradeWebSocket((c) => {
return {
onMessage(event, ws) {
console.log(`Message from client: ${event.data}`)
ws.send('Hello from server!')
},
onClose: () => {
console.log('Connection closed')
},
}
})
)
```
使えるイベント一覧:
- `onOpen` - Cloudflare Workers では現在動きません
- `onMessage`
- `onClose`
- `onError`
::: warning
WebSocket ヘルパーを使用するルートでヘッダを変更するミドルウェア ( CORS など) を使用している場合、イミュータブルなヘッドを変更できないというエラーが発生する可能性があります。 これは、 `upgradeWebSocket()` が内部的にヘッダを変更するためです。
なので、 WebSocket ヘルパーとミドルウェアを同時に使用している場合は注意してください。
:::
## RPC モード
WebSocket ヘルパーで定義されたハンドラーは、 RPC モードをサポートしています。
```ts
// server.ts
const wsApp = app.get(
'/ws',
upgradeWebSocket((c) => {
//...
})
)
export type WebSocketApp = typeof wsApp
// client.ts
const client = hc('http://localhost:8787')
const socket = client.ws.$ws() // A WebSocket object for a client
```
## 例
WebSocket を使った例:
### サーバーとクライアント
```ts
// server.ts
import { Hono } from 'hono'
import { upgradeWebSocket } from '@hono/cloudflare-workers'
const app = new Hono().get(
'/ws',
upgradeWebSocket(() => {
return {
onMessage: (event) => {
console.log(event.data)
},
}
})
)
export default app
```
```ts
// client.ts
import { hc } from 'hono/client'
import type app from './server'
const client = hc('http://localhost:8787')
const ws = client.ws.$ws(0)
ws.addEventListener('open', () => {
setInterval(() => {
ws.send(new Date().toString())
}, 1000)
})
```
### Bun で JSX を使用する場合
```tsx
import { Hono } from 'hono'
import { upgradeWebSocket, websocket } from '@hono/bun'
import { html } from 'hono/html'
const app = new Hono()
app.get('/', (c) => {
return c.html(
{html`
`}
)
})
const ws = app.get(
'/ws',
upgradeWebSocket((c) => {
let intervalId
return {
onOpen(_event, ws) {
intervalId = setInterval(() => {
ws.send(new Date().toString())
}, 200)
},
onClose() {
clearInterval(intervalId)
},
}
})
)
export default {
fetch: app.fetch,
websocket,
}
```
### Node.js
```ts
import { serve, upgradeWebSocket } from '@hono/node-server'
import { Hono } from 'hono'
import { WebSocketServer } from 'ws'
const app = new Hono()
app.get(
'/ws',
upgradeWebSocket(() => ({
onMessage(event, ws) {
ws.send(event.data)
},
}))
)
const wss = new WebSocketServer({ noServer: true })
serve({
fetch: app.fetch,
websocket: { server: wss },
})
```
# ベストプラクティス
Hono はとても柔軟です。 あなたの好きなようにアプリを書くことが出来ます。
しかし、従ったほうが良いベストプラクティスもあります。
## できるだけ "コントローラー" を作らないでください
極力、 "Ruby on Rails のようなコントローラー" は作るべきではありません。
```ts
// 🙁
// A RoR-like Controller
const booksList = (c: Context) => {
return c.json('list books')
}
app.get('/books', booksList)
```
問題は型に関係しています。 例えば、複雑なジェネリクスを書かない限り、コントローラーではパスパラメータを推論できません。
```ts
// 🙁
// A RoR-like Controller
const bookPermalink = (c: Context) => {
const id = c.req.param('id') // Can't infer the path param
return c.json(`get ${id}`)
}
```
そのため、 RoR-like なコントローラーを作る必要はなく、パス定義の直後にハンドラを書くべきです。
```ts
// 😃
app.get('/books/:id', (c) => {
const id = c.req.param('id') // Can infer the path param
return c.json(`get ${id}`)
})
```
## `hono/factory` の `factory.createHandlers()`
それでも RoR-like なコントローラーを作りたい場合、 [`hono/factory`](/docs/helpers/factory) の `factory.createHandlers()` を使ってください。 これを使う場合、型推論は正しく動作します。
```ts
import { createFactory } from 'hono/factory'
import { logger } from 'hono/logger'
// ...
// 😃
const factory = createFactory()
const middleware = factory.createMiddleware(async (c, next) => {
c.set('foo', 'bar')
await next()
})
const handlers = factory.createHandlers(logger(), middleware, (c) => {
return c.json(c.var.foo)
})
app.get('/api', ...handlers)
```
## 大きなアプリケーションを作る
"Ruby on Rails のようなコントローラー" を作ること無く大きなアプリケーションを作るには `app.route()` を使います。
アプリケーションに `/authors` と `/books` というエンドポイントがあって `index.ts` を分割したい場合は `authors.ts` と `books.ts` を作成します。
```ts
// authors.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.json('list authors'))
app.post('/', (c) => c.json('create an author', 201))
app.get('/:id', (c) => c.json(`get ${c.req.param('id')}`))
export default app
```
```ts
// books.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.json('list books'))
app.post('/', (c) => c.json('create a book', 201))
app.get('/:id', (c) => c.json(`get ${c.req.param('id')}`))
export default app
```
次に、それらをインポートし `app.route()` で `/authors` と `/books` をマウントします。
```ts
// index.ts
import { Hono } from 'hono'
import authors from './authors'
import books from './books'
const app = new Hono()
// 😃
app.route('/authors', authors)
app.route('/books', books)
export default app
```
### RPC 機能を使いたい場合
上のコードは普通の使い方ではうまく動きます。
しかし、 `RPC` 機能を使いたい場合は以下のように変更することで正しい型にすることができます。
```ts
// authors.ts
import { Hono } from 'hono'
const app = new Hono()
.get('/', (c) => c.json('list authors'))
.post('/', (c) => c.json('create an author', 201))
.get('/:id', (c) => c.json(`get ${c.req.param('id')}`))
export default app
export type AppType = typeof app
```
`app` の型を `hc` に渡すことで、正しい型になります。
```ts
import type { AppType } from './authors'
import { hc } from 'hono/client'
// 😃
const client = hc('http://localhost') // Typed correctly
```
詳しくは、 [RPC のページ](/docs/guides/rpc#using-rpc-with-larger-applications) を御覧ください。
## HEAD リクエストのベストプラクティス
### Hono の HEAD 処理を理解する
Hono は、自動的に HEAD リクエストを GET リクエストに変更し、レスポンスボディを除去することで HEAD リクエストを処理します。 この振る舞いはフレームワークのディスパッチ層に組み込まれており、ルートマッチングが起きる前に行われます。
### ✅ 動作する: HEAD リクエスト用に GET ルートを使用する
```typescript
// GOOD: この GET ルートは自動的に HEAD リクエストを処理します
app.get('/api/users', async (c) => {
const users = await getUsers()
c.header('X-Total-Count', users.length.toString())
return c.json(users)
})
// HEAD /api/users リクエストは次の内容を返します:
// - GET と同じヘッダ (X-Total-Count を含む)
// - Status 200
// - No body (null)
```
### ✅ 動作する: HEAD 固有のロジック用にミドルウェアを使用する
```typescript
// GOOD: HEAD が異なる振る舞いが必要な際にミドルウェアを使用する
app.use('/api/resource', async (c, next) => {
await next()
// ハンドラの後処理で HEAD 固有のヘッダを追加
if (c.req.method === 'HEAD') {
c.header('X-HEAD-Processed', 'true')
// HEAD 用に過度なボディの内容を計算しない
c.res = new Response(null, c.res)
}
})
```
### ❌ 動作しない: HEAD 専用のハンドラを生成してみる
```typescript
// BAD: これは期待通りには動作しません
app.head('/api/users', (c) => {
// このハンドラは決して呼ばれません
c.header('X-Custom', 'value')
return c.text('ignored')
})
// BAD: on() を使用しても動作しません
app.on('HEAD', '/api/users', (c) => {
// ルートマッチングする前に GET に変換されます
})
```
### パフォーマンスの考慮
- **多くの HEAD リクエストを期待している場合、 GET ハンドラで重い処理を避ける**: HEAD を検知し、ボディの生成をスキップするためにミドルウェアを使用します
- **キャッシュヘッダは同様に動作します**: HEAD レスポンスは GET と同じキャッシュルールに従います
- **ミドルウェアの互換性**: 大抵のミドルウェアは、 HEAD を処理します。 しかし、(圧縮のような) ボディを処理するミドルウェアは自動的に HEAD リクエストをスキップします
### HEAD リクエストをテストする
```typescript
// 常に GET と HEAD 両方のレスポンスをテストします
it('handles HEAD requests correctly', async () => {
const getRes = await app.request('/api/users')
const headRes = await app.request('/api/users', { method: 'HEAD' })
expect(headRes.status).toBe(getRes.status)
expect(headRes.headers.get('X-Total-Count')).toBe(
getRes.headers.get('X-Total-Count')
)
expect(headRes.body).toBe(null)
})
```
### 注記
- 自動的な HEAD の変換は、GET と HEAD レスポンス間でヘッダの一貫性を保証します
- この振る舞いは、すべての Hono ランタイム (Cloudflare Workers, Deno, Bun, Node.js) で一貫性があります
- HEAD と GET で全く異なるロジックが必要な場合、フレームワークの HEAD 処理をオーバーライドするよりもエンドポイントを分けて使用することを検討してください
# Create-hono
`create-hono` でサポートされるコマンドラインオプションです。 `npm create hono@latest`, `npx create-hono@latest`, `pnpm create hono@latest` を実行すると、プロジェクトを初期化します。
> [!NOTE]
> **なぜこのページがあるのか?** インストールやクイックスタートのサンプルでは、 `npm create hono@latest my-app` のような最小限のコマンドが示されています。 `create-hono` はいくつかの有用なフラグをサポートしており、自動化やカスタマイズしたプロジェクト作成 (テンプレートの選択・プロンプトのスキップ・パッケージマネージャの選択・ローカルキャッシュの使用など) をすることができます。
## 引数を渡す:
`npm create` (あるいは `npx`) を使用する際、初期化スクリプトに渡す引数は `--` **の後に** 置かなければなりません。 `--` の後のすべてのものが初期化スクリプトに送られます。
::: code-group
```sh [npm]
# create-hono (npm は `--` が必要です) に引数を送ります
npm create hono@latest my-app -- --template cloudflare-workers
```
```sh [yarn]
# "--template cloudflare-workers" は Cloudflare Workers テンプレートを選択します
yarn create hono my-app --template cloudflare-workers
```
```sh [pnpm]
# "--template cloudflare-workers" は Cloudflare Workers テンプレートを選択します
pnpm create hono@latest my-app --template cloudflare-workers
```
```sh [bun]
# "--template cloudflare-workers" は Cloudflare Workers テンプレートを選択します
bun create hono@latest my-app --template cloudflare-workers
```
```sh [deno]
# "--template cloudflare-workers" は Cloudflare Workers テンプレートを選択します
deno init --npm hono@latest my-app --template cloudflare-workers
```
:::
## 一般的に使用される引数
| Argument | Description | Example |
| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------ |
| `--template ` | 開始テンプレートを選択して、インタラクティブなテンプレートプロンプトをスキップします。 テンプレートは、 `bun`, `cloudflare-workers`, `vercel` のような名称がある可能性があります。 | `--template cloudflare-workers` |
| `--install` | テンプレートを生成後に、自動的に依存関係をインストールします。 | `--install` |
| `--pm ` | 依存関係をインストールする際に、どのパッケージマネージャを実行するかを指定します。 一般的な値: `npm`, `pnpm`, `yarn` | `--pm pnpm` |
| `--offline` | 最新のリモートテンプレートを取得する代わりに、ローカルのキャッシュまたはテンプレートを使用します。 オフライン環境やローカル実行が決まっている場合に有用です。 | `--offline` |
> [!NOTE]
> 正確なテンプレートのセットや有効なオプションは、 `create-hono` プロジェクトでメンテナンスされています。 この文書では、よく使用されるフラグをまとめています。 完全で信頼のあるリファレンスは、以下のリポジトリを参照してください。
## フローのサンプル
### インタラクティブを最小限に
```bash
npm create hono@latest my-app
```
これはテンプレートとオプションを掲示します。
### インタラクティブなしで、テンプレートとパッケージマネージャを選択する
```bash
npm create hono@latest my-app -- --template vercel --pm npm --install
```
これは、 `vercel` テンプレートを使用して `my-app` を生成し、 `npm` を使用して依存関係をインストールします。 インタラクティブなプロンプトはスキップします。
### オフラインキャッシュを使用する (ネットワークを使用しない)
```bash
pnpm create hono@latest my-app --template deno --offline
```
## トラブルシューティングとコツ
- オプションが認識されていない場合、 `npm create` / `npx` を使用する際に `--` でオプションを送っているかどうかを確認してください
- テンプレートやフラグの最新のリストを確認するには、 `create-hono` リポジトリを確認するか、ローカルで初期化スクリプトを実行してヘルプの出力に従ってください
## リンクとリファレンス
- `create-hono` repository : [create-hono](https://github.com/honojs/create-hono)
# 例
[使用例の一覧](/examples/)をご覧ください。
# よくある質問
Hono のよくある質問 (FAQ) と解決方法をまとめたガイド。
## Hono 公式の Renovate 設定はありますか?
Hono チームは現在 [Renovate](https://github.com/renovatebot/renovate) の設定をメンテナンスしていません。
そのため、下のようにサードパーティーの renovate-config を使用してください。
`renovate.json` :
```json
// renovate.json
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>shinGangan/renovate-config-hono" // [!code ++]
]
}
```
[renovate-config-hono](https://github.com/shinGangan/renovate-config-hono) リポジトリで詳細を確認してください。
# ヘルパー
アプリケーションの開発を手助けするヘルパーがあります。 ミドルウェアと違って、ハンドラとして機能するのではなく、便利な関数を提供します。
例えば、 [Cookie helper](/docs/helpers/cookie) の使い方は以下のとおりです。
```ts
import { getCookie, setCookie } from 'hono/cookie'
const app = new Hono()
app.get('/cookie', (c) => {
const yummyCookie = getCookie(c, 'yummy_cookie')
// ...
setCookie(c, 'delicious_cookie', 'macha')
//
})
```
## 利用可能なヘルパー
- [Accepts](/docs/helpers/accepts)
- [Adapter](/docs/helpers/adapter)
- [Cookie](/docs/helpers/cookie)
- [css](/docs/helpers/css)
- [Dev](/docs/helpers/dev)
- [Factory](/docs/helpers/factory)
- [html](/docs/helpers/html)
- [JWT](/docs/helpers/jwt)
- [SSG](/docs/helpers/ssg)
- [Streaming](/docs/helpers/streaming)
- [Testing](/docs/helpers/testing)
- [WebSocket](/docs/helpers/websocket)
# クライアントコンポーネント
`hono/jsx` はサーバサイドだけでなくクライアントサイドもサポートします。 このため、ブラウザで実行するインタラクティブな UI を生成することができます。 クライアントコンポーネントまたは `hono/jsx/dom` と呼んでいます。
とても高速で、サイズが小さいです。 `hono/jsx/dom` で作ったカウンタープログラムは、 Brotli 圧縮でわずか 2.8KB です。 React 用は 47.8KB になります。
このセクションではクライアントコンポーネント特有の機能を紹介します。
## カウンターサンプル
シンプルなカウンターサンプルです。 同じコードは、 React でも動作します。
```tsx
import { useState } from 'hono/jsx'
import { render } from 'hono/jsx/dom'
function Counter() {
const [count, setCount] = useState(0)
return (
Count: {count}
)
}
function App() {
return (
)
}
const root = document.getElementById('root')
render(, root)
```
## `render()`
指定された HTML 要素の中に JSX コンポーネントを挿入するために、 `render()` を使用することができます。
```tsx
render(, container)
```
[Counter サンプル](https://github.com/honojs/examples/tree/main/hono-vite-jsx) ですべてのサンプルコードを参照することができます。
## React と互換性のあるフック
hono/jsx/dom にはフックがあります。 これらは、 React と完全に互換性があるか、または部分的に互換性があります。 [React のドキュメント](https://react.dev/reference/react/hooks) を参照してこれらの API を学ぶことができます。
- `useState()`
- `useEffect()`
- `useRef()`
- `useCallback()`
- `use()`
- `startTransition()`
- `useTransition()`
- `useDeferredValue()`
- `useMemo()`
- `useLayoutEffect()`
- `useReducer()`
- `useDebugValue()`
- `createElement()`
- `memo()`
- `isValidElement()`
- `useId()`
- `createRef()`
- `forwardRef()`
- `useImperativeHandle()`
- `useSyncExternalStore()`
- `useInsertionEffect()`
- `useFormStatus()`
- `useActionState()`
- `useOptimistic()`
## `startViewTransition()` ファミリー
`startViewTransition()` ファミリーは、 [View Transitions API](https://developer.mozilla.org/ja/docs/Web/API/View_Transitions_API) を簡単に扱うためのオリジナルのフックや関数があります。 以下の例では、どのように使用するかを示しています。
### 1. とてもシンプルな例
`startViewTransition()` を使って簡単に `document.startViewTransition` を使用するトランジションを記述できます。
```tsx
import { useState, startViewTransition } from 'hono/jsx'
import { css, Style } from 'hono/css'
export default function App() {
const [showLargeImage, setShowLargeImage] = useState(false)
return (
<>
{!showLargeImage ? (

) : (
)}
>
)
}
```
### 2. `keyframes()` と共に `viewTransition()` を使用する
`viewTransition()` 関数を使うと、 ユニークな `view-transition-name` を取得できます。
`keyframes()` でそれを使用することができ、 `::view-transition-old()` は `::view-transition-old(${uniqueName))` に変換されます。
```tsx
import { useState, startViewTransition } from 'hono/jsx'
import { viewTransition } from 'hono/jsx/dom/css'
import { css, keyframes, Style } from 'hono/css'
const rotate = keyframes`
from {
rotate: 0deg;
}
to {
rotate: 360deg;
}
`
export default function App() {
const [showLargeImage, setShowLargeImage] = useState(false)
const [transitionNameClass] = useState(() =>
viewTransition(css`
::view-transition-old() {
animation-name: ${rotate};
}
::view-transition-new() {
animation-name: ${rotate};
}
`)
)
return (
<>
{!showLargeImage ? (

) : (
)}
>
)
}
```
### 3. `useViewTransition` を使用する
アニメーション中だけスタイルを変更したい場合、 `useViewTransition()` を使用することができます。 このフックは `[boolean, (callback: () => void) => void]` を返します。 これは `isUpdating` フラグや `startViewTransition()` 関数です。
このフックを使用する際、コンポーネントは次の2箇所のタイミングで評価されます。
- `startViewTransition()` の呼び出しのコールバック内。
- [the `finish` promise becomes fulfilled](https://developer.mozilla.org/ja/docs/Web/API/ViewTransition/finished)
```tsx
import { useState, useViewTransition } from 'hono/jsx'
import { viewTransition } from 'hono/jsx/dom/css'
import { css, keyframes, Style } from 'hono/css'
const rotate = keyframes`
from {
rotate: 0deg;
}
to {
rotate: 360deg;
}
`
export default function App() {
const [isUpdating, startViewTransition] = useViewTransition()
const [showLargeImage, setShowLargeImage] = useState(false)
const [transitionNameClass] = useState(() =>
viewTransition(css`
::view-transition-old() {
animation-name: ${rotate};
}
::view-transition-new() {
animation-name: ${rotate};
}
`)
)
return (
<>
{!showLargeImage ? (

) : (
)}
>
)
}
```
## `hono/jsx/dom` ランタイム
クライアントコンポーネント向けの小さな JSX ランタイムがあります。 これを使用すると、 `hono/jsx` を使用する場合よりも小さなバンドル結果になります。 `tsconfig.json` で `hono/jsx/dom` を指定してください。 Deno の場合は deno.json を修正してください。
```json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx/dom"
}
}
```
または、 `vite.config.ts` の esbuild transform オプションで `hono/jsx/dom` を指定することもできます。
```ts
import { defineConfig } from 'vite'
export default defineConfig({
esbuild: {
jsxImportSource: 'hono/jsx/dom',
},
})
```
# JSX
`hono/jsx` で、 HTML を JSX 構文で書くことができます。
`hono/jsx` はクライアントでも動作しますが、サーバー側でコンテンツをレンダリングするとき最も頻繁に使うことになるでしょう。 ここでは、サーバーとクライアントの両方に共通する、 JSX に関するいくつかのことを説明します。
## 設定
JSX を使うために `tsconfig.json` を変更します:
`tsconfig.json`:
```json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}
```
あるいは、プラグマを使用します:
```ts
/** @jsx jsx */
/** @jsxImportSource hono/jsx */
```
Deno では、 `tsconfig.json` の代わりに `deno.json` を変更しなければなりません:
```json
{
"compilerOptions": {
"jsx": "precompile",
"jsxImportSource": "@hono/hono/jsx"
}
}
```
## 使い方
:::info
[Quick Start](/docs/#quick-start) から直接来られている場合、メインファイルは `.ts` という拡張子をもっています - これを `.tsx` に変える必要があります - そうしないとアプリケーションを実行できないでしょう。 さらに変更を反映するために `package.json` (あるいは Deno を使用しているなら `deno.json` ) を変更するべきです。 (たとえば、dev スクリプトを `bun run --hot src/index.ts` とする代わりに、 `bun run --hot src/index.tsx` とすべきです)
:::
`index.tsx`:
```tsx
import { Hono } from 'hono'
import type { FC } from 'hono/jsx'
const app = new Hono()
const Layout: FC = (props) => {
return (
{props.children}
)
}
const Top: FC<{ messages: string[] }> = (props: {
messages: string[]
}) => {
return (
Hello Hono!
{props.messages.map((message) => {
return - {message}!!
})}
)
}
app.get('/', (c) => {
const messages = ['Good Morning', 'Good Evening', 'Good Night']
return c.html()
})
export default app
```
## メタデータの巻き上げ
コンポーネント内に直接 ``, ``, `` などのドキュメントメタデータタグを記述することができます。 これらのタグは、ドキュメントの `` セクションに自動的に巻き上げられます。 `` 要素が適切なメタデータを決定するコンポーネントから描画されるときに特に有用です。
```tsx
import { Hono } from 'hono'
const app = new Hono()
app.use('*', async (c, next) => {
c.setRenderer((content) => {
return c.html(
{content}
)
})
await next()
})
app.get('/about', (c) => {
return c.render(
<>
About Page
about page content
>
)
})
export default app
```
:::info
巻き上げが起こるとき、既存の要素は削除されません。 後に現れる要素は最後に追加されます。 たとえば、 `` に `Default` があり、コンポーネントが `Page Title` を描画する場合、両方のタイトルがヘッダに現れます。
:::
## フラグメント
フラグメントを使用して、複数の要素を追加ノード無しでグループ化します:
```tsx
import { Fragment } from 'hono/jsx'
const List = () => (
first child
second child
third child
)
```
きちんと設定されていれば、 `<>>` を使って書くこともできます。
```tsx
const List = () => (
<>
first child
second child
third child
>
)
```
## `PropsWithChildren`
`PropsWithChildren` を使用すると、関数コンポーネント内の子要素を正しく推論できます。
```tsx
import { PropsWithChildren } from 'hono/jsx'
type Post = {
id: number
title: string
}
function Component({ title, children }: PropsWithChildren) {
return (
{title}
{children}
)
}
```
## 生 HTML の挿入
直接 HTML を挿入するには、 `dangerouslySetInnerHTML` を使用します:
```tsx
app.get('/foo', (c) => {
const inner = { __html: 'JSX · SSR' }
const Div =
})
```
## メモ
`memo` を使用して、計算済みの文字列を保存することでコンポーネントを最適化します:
```tsx
import { memo } from 'hono/jsx'
const Header = memo(() => )
const Footer = memo(() => )
const Layout = (
)
```
## Context
`useContext` を使用することで、プロパティを通して値を渡したりせずに、コンポーネントツリーのあらゆるレベルに渡ってデータをグローバルに共有することができます。
```tsx
import type { FC } from 'hono/jsx'
import { createContext, useContext } from 'hono/jsx'
const themes = {
light: {
color: '#000000',
background: '#eeeeee',
},
dark: {
color: '#ffffff',
background: '#222222',
},
}
const ThemeContext = createContext(themes.light)
const Button: FC = () => {
const theme = useContext(ThemeContext)
return
}
const Toolbar: FC = () => {
return (
)
}
// ...
app.get('/', (c) => {
return c.html(
)
})
```
## Async コンポーネント
`hono/jsx` は、 Async コンポーネントをサポートしているため、コンポーネント内で `async`/`await` を使用できます。
`c.html()` を使って描画する場合、自動的に await されます。
```tsx
const AsyncComponent = async () => {
await new Promise((r) => setTimeout(r, 1000)) // sleep 1s
return Done!
}
app.get('/', (c) => {
return c.html(
)
})
```
## Suspense
React と同じような `Suspense` 機能が使用できます。
`Suspense` を使って async コンポーネントをラップすると、 fallback 内のコンテンツが最初に描画されます。 一旦 Promise が resolve になると、await されたコンテンツが描画されます。
`renderToReadableStream()` メソッドで使用できます。
```tsx
import { renderToReadableStream, Suspense } from 'hono/jsx/streaming'
//...
app.get('/', (c) => {
const stream = renderToReadableStream(
loading...}>
)
return c.body(stream, {
headers: {
'Content-Type': 'text/html; charset=UTF-8',
'Transfer-Encoding': 'chunked',
},
})
})
```
## ErrorBoundary
`ErrorBoundary` を使用して子コンポーネントでエラーをキャッチすることができます。
以下の例では、エラーが発生した場合に、 `fallback` で指定されたコンテンツを表示します。
```tsx
function SyncComponent() {
throw new Error('Error')
return Hello
}
app.get('/sync', async (c) => {
return c.html(
Out of Service}>
)
})
```
`ErrorBoundary` を async コンポーネントや `Suspense` で使用することもできます。
```tsx
async function AsyncComponent() {
await new Promise((resolve) => setTimeout(resolve, 2000))
throw new Error('Error')
return Hello
}
app.get('/with-suspense', async (c) => {
return c.html(
Out of Service}>
Loading...}>
)
})
```
## StreamingContext
`Suspense` や `ErrorBoundary` のようなストリーミングコンポーネントの設定を提供する `StreamingContext` を使用することができます。 コンテンツセキュリティポリシー (CSP) のためにこれらのコンポーネントによって生成されるスクリプトタグに使い捨ての値を追加するのに有用です。
```tsx
import { Suspense, StreamingContext } from 'hono/jsx/streaming'
// ...
app.get('/', (c) => {
const stream = renderToReadableStream(
Loading...}>
)
return c.body(stream, {
headers: {
'Content-Type': 'text/html; charset=UTF-8',
'Transfer-Encoding': 'chunked',
'Content-Security-Policy':
"script-src 'nonce-random-nonce-value'",
},
})
})
```
`scriptNonce` の値は、自動的に `Suspense` や `ErrorBoundary` で生成されたすべての `
) : (
)}
Hello
)
})
```
スクリプトをうまくビルドするために、下に示すような設定の例を `vite.config.ts` に使うことができます。
```ts
import pages from '@hono/vite-cloudflare-pages'
import devServer from '@hono/vite-dev-server'
import { defineConfig } from 'vite'
export default defineConfig(({ mode }) => {
if (mode === 'client') {
return {
build: {
rollupOptions: {
input: './src/client.ts',
output: {
entryFileNames: 'static/client.js',
},
},
},
}
} else {
return {
plugins: [
pages(),
devServer({
entry: 'src/index.tsx',
}),
],
}
}
})
```
次のコマンドを実行して、サーバーとクライアントスクリプトをビルドします。
```sh
vite build --mode client && vite build
```
## Cloudflare Pages のミドルウェア
Cloudflare Pages は Hono とは違う[ミドルウェア](https://developers.cloudflare.com/pages/functions/middleware/)システムを持っています。 `_middleware.ts` で `onRequest` を export することで有効化できます:
```ts
// functions/_middleware.ts
export async function onRequest(pagesContext) {
console.log(`You are accessing ${pagesContext.request.url}`)
return await pagesContext.next()
}
```
`handleMiddleware` を使うことで、 Hono のミドルウェアを Cloudflare Pages のミドルウェアとして使うことができます。
```ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'
export const onRequest = handleMiddleware(async (c, next) => {
console.log(`You are accessing ${c.req.url}`)
await next()
})
```
Hono のビルトイン、サードパーティミドルウェアも使うことができます。 例えば、 Basic 認証を追加するために [Hono の Basic 認証ミドルウェア](/docs/middleware/builtin/basic-auth) を使うことができます。
```ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'
import { basicAuth } from 'hono/basic-auth'
export const onRequest = handleMiddleware(
basicAuth({
username: 'hono',
password: 'acoolproject',
})
)
```
このように複数のミドルウェアを使うこともできます:
```ts
import { handleMiddleware } from 'hono/cloudflare-pages'
// ...
export const onRequest = [
handleMiddleware(middleware1),
handleMiddleware(middleware2),
handleMiddleware(middleware3),
]
```
### `EventContext` へのアクセス
[`EventContext`](https://developers.cloudflare.com/pages/functions/api-reference/#eventcontext) オブジェクトには `handleMiddleware` の `c.env` からアクセスできます。
```ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'
export const onRequest = [
handleMiddleware(async (c, next) => {
c.env.eventContext.data.user = 'Joe'
await next()
}),
]
```
次に、ハンドラでは `c.env.eventContext` からセットしたデータにアクセスできます:
```ts
// functions/api/[[route]].ts
import type { EventContext } from 'hono/cloudflare-pages'
import { handle } from 'hono/cloudflare-pages'
// ...
type Env = {
Bindings: {
eventContext: EventContext
}
}
const app = new Hono().basePath('/api')
app.get('/hello', (c) => {
return c.json({
message: `Hello, ${c.env.eventContext.data.user}!`, // 'Joe'
})
})
export const onRequest = handle(app)
```
# Cloudflare Workers + Vite
[`@cloudflare/vite-plugin`](https://developers.cloudflare.com/workers/vite-plugin/) を使用して、 [Vite](https://vite.dev) と共に [Cloudflare Workers](https://workers.cloudflare.com) 上にフルスタックアプリケーションを構築することができます。
このセットアップは、高速な Vite 開発サーバ、 Hono の JSX レンダラを使用したサーバサイドレンダリング、 Vite にバンドルされたクライアントサイドスクリプトなどを提供します - すべて Cloudflare Workers 上で動作します。
これは、 Cloudflare 上に新しいフルスタックプロジェクトを開始するために推奨されている方法です。
## 1. セットアップ
Vite を含んだ Cloudflare Workers 用のスターターが使用可能です。
"create-hono" コマンドを使って、プロジェクトを開始します。
この例では、 `cloudflare-workers+vite` を選択しています。
::: code-group
```sh [npm]
npm create hono@latest my-app
```
```sh [yarn]
yarn create hono my-app
```
```sh [pnpm]
pnpm create hono my-app
```
```sh [bun]
bun create hono@latest my-app
```
```sh [deno]
deno init --npm hono my-app
```
:::
`my-app` に移動し、依存関係をインストールします。
::: code-group
```sh [npm]
cd my-app
npm i
```
```sh [yarn]
cd my-app
yarn
```
```sh [pnpm]
cd my-app
pnpm i
```
```sh [bun]
cd my-app
bun i
```
:::
以下は基本的なディレクトリ構成です。
```text
./
├── package.json
├── public // Put your static files here.
├── src
│ ├── index.tsx // The entry point for server-side.
│ ├── renderer.tsx
│ └── style.css
├── tsconfig.json
├── vite.config.ts
└── wrangler.jsonc
```
`vite.config.ts` は、 Cloudflare プラグインと SSR 用の `vite-ssr-components` を結合します:
```ts
import { cloudflare } from '@cloudflare/vite-plugin'
import { defineConfig } from 'vite'
import ssrPlugin from 'vite-ssr-components/plugin'
export default defineConfig({
plugins: [cloudflare(), ssrPlugin()],
})
```
## 2. Hello World
次のように `src/index.tsx` を編集します:
```tsx
import { Hono } from 'hono'
import { renderer } from './renderer'
const app = new Hono()
app.use(renderer)
app.get('/', (c) => {
return c.render(Hello, Cloudflare Workers!
)
})
export default app
```
`renderer` は、 `vite-ssr-components` といっしょに Hono の [JSX renderer ミドルウェア](/docs/middleware/builtin/jsx-renderer) を使用して `src/renderer.tsx` で定義されます。
```tsx
import { jsxRenderer } from 'hono/jsx-renderer'
import { Link, ViteClient } from 'vite-ssr-components/hono'
export const renderer = jsxRenderer(({ children }) => {
return (
{children}
)
})
```
## 3. 実行
開発サーバをローカルで実行します。 ウェブブラウザで `http://localhost:5173` にアクセスします。
::: code-group
```sh [npm]
npm run dev
```
```sh [yarn]
yarn dev
```
```sh [pnpm]
pnpm dev
```
```sh [bun]
bun run dev
```
:::
## 4. デプロイ
Cloudflare アカウントを持っている場合、 Cloudflare にデプロイすることができます。 `deploy` スクリプトは、 Vite でビルドされ、 Wrangler で公開します。
::: code-group
```sh [npm]
npm run deploy
```
```sh [yarn]
yarn deploy
```
```sh [pnpm]
pnpm run deploy
```
```sh [bun]
bun run deploy
```
:::
## バインディング
Variables, KV, D1 などのような Cloudflare バインディングを使用することができます。
`wrangler.jsonc` で設定します。 たとえば、 `MY_NAME` という名前の変数を追加するには以下のようにします:
```jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-app",
"compatibility_date": "2025-08-03",
"main": "./src/index.tsx",
"vars": {
"MY_NAME": "Hono",
},
}
```
バインディング用に型を生成するために、 `cf-typegen` スクリプトを実行します:
::: code-group
```sh [npm]
npm run cf-typegen
```
```sh [yarn]
yarn cf-typegen
```
```sh [pnpm]
pnpm run cf-typegen
```
```sh [bun]
bun run cf-typegen
```
:::
`CloudflareBindings` が生成されるので、ジェネリクスとして `Hono` にそれを渡します:
```ts
const app = new Hono<{ Bindings: CloudflareBindings }>()
```
`c.env` を通してバインディングにアクセスします:
```tsx
app.get('/', (c) => {
return c.render(Hello! {c.env.MY_NAME}
)
})
```
## クライアントサイド
`vite-ssr-components` は、 Vite を通してクライアントサイドスクリプトをロードさせます。
クライアントのエントリポイントを示す `Script` コンポーネントを追加してください。 Vite は、 dev と production のどちらでもバンドルを処理します:
```tsx
import { jsxRenderer } from 'hono/jsx-renderer'
import { Script, ViteClient } from 'vite-ssr-components/hono'
export const renderer = jsxRenderer(({ children }) => {
return (
{children}
)
})
```
詳細については、 [`@cloudflare/vite-plugin` documentation](https://developers.cloudflare.com/workers/vite-plugin/) や [`vite-ssr-components`](https://github.com/yusukebe/vite-ssr-components) を参照してください。
# Cloudflare Workers
[Cloudflare Workers](https://workers.cloudflare.com) は Cloudflare の CDN で JavaScript を実行するエッジランタイムです。
アプリケーションをローカルで開発し、 [Wrangler](https://developers.cloudflare.com/workers/wrangler/) のいくつかのコマンドを使用して公開します。
Wrangler はコンパイラを内蔵しているため、 TypeScript でコードを書けます。
Hono を使った Cloudflare Workers 最初のアプリケーションを作りましょう。
## 1. セットアップ
Cloudflare Workers 向けのスターターが使用できます。
"create-hono" コマンドでプロジェクトを作成してください。
`cloudflare-workers` テンプレートを選択します。
::: code-group
```sh [npm]
npm create hono@latest my-app
```
```sh [yarn]
yarn create hono my-app
```
```sh [pnpm]
pnpm create hono my-app
```
```sh [bun]
bun create hono@latest my-app
```
```sh [deno]
deno init --npm hono my-app
```
:::
`my-app` に移動し、依存関係をインストールします。
::: code-group
```sh [npm]
cd my-app
npm i
```
```sh [yarn]
cd my-app
yarn
```
```sh [pnpm]
cd my-app
pnpm i
```
```sh [bun]
cd my-app
bun i
```
:::
## 2. Hello World
`src/index.ts` をこのように変更します。
```ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Cloudflare Workers!'))
export default app
```
## 3. Run
ローカル開発サーバーを起動し、ブラウザで `http://localhost:8787` にアクセスします。
::: code-group
```sh [npm]
npm run dev
```
```sh [yarn]
yarn dev
```
```sh [pnpm]
pnpm dev
```
```sh [bun]
bun run dev
```
:::
### ポート番号を変える
ポート番号を変える必要がある場合は、 `wrangler.toml` / `wrangler.json` / `wrangler.jsonc` を以下のドキュメントに従って変更してください:
[Wrangler Configuration](https://developers.cloudflare.com/workers/wrangler/configuration/#local-development-settings)
もしくは CLI オプションで設定することもできます:
[Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/commands/#dev)
## 4. デプロイ
Cloudflare アカウントを持っている場合、 Cloudflare にデプロイ出来ます。 `package.json` の `$npm_execpath` を選択したパッケージマネージャに置き換える必要があります。
::: code-group
```sh [npm]
npm run deploy
```
```sh [yarn]
yarn deploy
```
```sh [pnpm]
pnpm run deploy
```
```sh [bun]
bun run deploy
```
:::
それだけです!
## 他のイベントハンドラとともに Hono を使う
_Module Worker モード_ で他のイベントハンドラ ( `scheduled` など ) を統合できます。
このように、 `app.fetch` を `fetch` ハンドラとしてエクスポートし、必要に応じて他のハンドラも実装します:
```ts
const app = new Hono()
export default {
fetch: app.fetch,
scheduled: async (batch, env) => {},
}
```
## 静的ファイルの提供
静的ファイルを提供したい場合、 Cloudflare Workers の [Static Assets 機能](https://developers.cloudflare.com/workers/static-assets/) を使うことができます。 `wrangler.jsonc` でファイルを置くディレクトリを指定します:
```jsonc
"assets": { "directory": "public" }
```
次に `public` ディレクトリを作成し、ファイルを設置します. 例えば、 `./public/static/hello.txt` は `/static/hello.txt` として提供されます。
```
.
├── package.json
├── public
│ ├── favicon.ico
│ └── static
│ └── hello.txt
├── src
│ └── index.ts
└── wrangler.jsonc
```
## 型
Workers の型が欲しい場合は `@cloudflare/workers-types` をインストールする必要があります。
::: code-group
```sh [npm]
npm i --save-dev @cloudflare/workers-types
```
```sh [yarn]
yarn add -D @cloudflare/workers-types
```
```sh [pnpm]
pnpm add -D @cloudflare/workers-types
```
```sh [bun]
bun add --dev @cloudflare/workers-types
```
:::
## テスト
テストのために `@cloudflare/vitest-pool-workers` が推奨されます。
[例](https://github.com/honojs/examples)を読んで設定してください。
このようなアプリケーションに対して
```ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Please test me!'))
```
このコードを使用して "_200 OK_" レスポンスが返されるかをテストします。
```ts
describe('Test the application', () => {
it('Should return 200 response', async () => {
const res = await app.request('http://localhost/')
expect(res.status).toBe(200)
})
})
```
## バインディング
Cloudflare Workers では環境変数、 KV ネームスペース、 R2 バケット、 Durable Object などをバインドし、 `c.env` からアクセスできます。 `Hono` のジェネリクスとしてバインディングの型定義を渡します。
```ts
type Bindings = {
MY_BUCKET: R2Bucket
USERNAME: string
PASSWORD: string
}
const app = new Hono<{ Bindings: Bindings }>()
// Access to environment values
app.put('/upload/:key', async (c, next) => {
const key = c.req.param('key')
await c.env.MY_BUCKET.put(key, c.req.body)
return c.text(`Put ${key} successfully!`)
})
```
### 自動的にバインディングの型を生成する
手動でバインディングの型を定義する代わりに、 `wrangler types` コマンドを使用して、 `wrangler.toml` から自動生成することができます。 Hono のビルトインである `Env` 型との名前の衝突を避けるために `--env-interface` フラグを使用してください。
```sh
wrangler types --env-interface CloudflareBindings
```
指定したインタフェース名をもつ `worker-configuration.d.ts` ファイルを生成します。 これを Hono に渡します:
```ts
const app = new Hono<{ Bindings: CloudflareBindings }>()
app.put('/upload/:key', async (c, next) => {
const key = c.req.param('key')
await c.env.MY_BUCKET.put(key, c.req.body)
return c.text(`Put ${key} successfully!`)
})
```
## ミドルウェアで環境変数を使用する
Module Worker モードのみで使用できます。
Basic 認証のユーザー名やパスワードなど、ミドルウェア内で環境変数やシークレットを使用したい場合はこのように書きます。
```ts
import { basicAuth } from 'hono/basic-auth'
type Bindings = {
USERNAME: string
PASSWORD: string
}
const app = new Hono<{ Bindings: Bindings }>()
//...
app.use('/auth/*', async (c, next) => {
const auth = basicAuth({
username: c.env.USERNAME,
password: c.env.PASSWORD,
})
return auth(c, next)
})
```
同じように Bearer 認証や JWT 認証などもできます。
## Github Actions からデプロイする
CI で Cloudflare にデプロイする前に、 Cloudflare のトークンが必要です。 [User API Tokens](https://dash.cloudflare.com/profile/api-tokens) で管理できます。
新しく作られたトークンでは、 **Edit Cloudflare Workers** テンプレートを選択し、すでにトークンがある場合は、トークンが対応する権限を持っていることを確認してください。
次に GitHub リポジトリの設定ダッシュボードで `Settings->Secrets and variables->Actions->Repository secrets` を開き、 `CLOUDFLARE_API_TOKEN` という名前のシークレットを作成します。
`.github/workflows/deploy.yml` を Hono プロジェクトのルートフォルダに作成し、以下のコードを貼り付けます:
```yml
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v4
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```
次に `wrangler.jsonc` を編集し、 `compatibility_date` の行の後に次のコードを追加します。
```jsonc
"main": "src/index.ts",
"minify": true
```
準備が整いました! 後はコードを push して楽しんでください。
## ローカル開発環境で環境変数をロードする
ローカル開発環境で環境変数を設定するには、 `.dev.vars` か `.env` ファイルをプロジェクトのルートディレクトリに作成します。
これらのファイルは [dotenv](https://hexdocs.pm/dotenvy/dotenv-file-format.html) の構文を使用します。 以下に例を示します:
```
SECRET_KEY=value
API_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
```
> 詳しくは Cloudflare のドキュメントをご覧ください:
> https://developers.cloudflare.com/workers/wrangler/configuration/#secrets
コードの中で `c.env.*` から環境変数にアクセスします。
::: info
デフォルトでは、 `process.env` は Cloudflare Workers では利用できないため、環境変数は `c.env` から取得することが推奨されます。 使用したい場合は、 [`nodejs_compat_populate_process_env`](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#enable-auto-populating-processenv) フラグを有効にする必要があります。 また、 `cloudflare:workers` から `env` をインポートすることもできます。 詳細は [Cloudflare docs の How to access `env`](https://developers.cloudflare.com/workers/runtime-apis/bindings/#how-to-access-env) をご覧ください。
:::
```ts
type Bindings = {
SECRET_KEY: string
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/env', (c) => {
const SECRET_KEY = c.env.SECRET_KEY
return c.text(SECRET_KEY)
})
```
Cloudflare にプロジェクトをデプロイする前に、環境変数、シークレットを Cloudflare Workers プロジェクトの設定で追加することを忘れないでください。
> 詳しくは Cloudflare のドキュメントをご覧ください:
> https://developers.cloudflare.com/workers/configuration/environment-variables/#add-environment-variables-via-the-dashboard
# Deno
[Deno](https://deno.com/) は V8 上に構築された JavaScript ランタイムです。 Node.js ではありません。
Hono は Deno でも動作します。
Hono を使用して TypeScript でコードを書き、 `deno` コマンドでアプリケーションを起動します。 そして "Deno Deploy" にデプロイ出来ます。
## 1. Deno のインストール
まず `deno` コマンドをインストールします。
[公式ドキュメント](https://docs.deno.com/runtime/getting_started/installation/) を参照してください。
## 2. セットアップ
Deno でもスターターを使用できます。
[`deno init`](https://docs.deno.com/runtime/reference/cli/init/) コマンドでプロジェクトを作成してください。
```sh
deno init --npm hono --template=deno my-app
```
`my-app` に移動しますが、 Deno では Hono を明示的にインストールする必要はありません。
```sh
cd my-app
```
## 3. Hello World
`main.ts` を変更します:
```ts [main.ts]
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Deno!'))
Deno.serve(app.fetch)
```
## 4. Run
ローカルで開発サーバーを実行します。 次に、 Web ブラウザで `http://localhost:8000` にアクセスします。
```sh
deno task start
```
## ポートを変える
`main.ts` の `Deno.serve` の引数を変更することでポート番号を指定できます:
```ts
Deno.serve(app.fetch) // [!code --]
Deno.serve({ port: 8787 }, app.fetch) // [!code ++]
```
## 静的ファイルの提供
静的ファイルを提供するには `@hono/deno` から `serveStatic` をインポートして使用します。
```sh
deno add jsr:@hono/deno
```
```ts
import { Hono } from 'hono'
import { serveStatic } from '@hono/deno'
const app = new Hono()
app.use('/static/*', serveStatic({ root: './' }))
app.use('/favicon.ico', serveStatic({ path: './favicon.ico' }))
app.get('/', (c) => c.text('You can access: /static/hello.txt'))
app.get('*', serveStatic({ path: './static/fallback.txt' }))
Deno.serve(app.fetch)
```
上のコードは、このようなディレクトリ構成で機能します。
```
./
├── favicon.ico
├── index.ts
└── static
├── demo
│ └── index.html
├── fallback.txt
├── hello.txt
└── images
└── dinotocat.png
```
### `rewriteRequestPath`
`http://localhost:8000/static/*` を `./statics` にマップしたい場合、 `rewriteRequestPath` をオプションに追加してください:
```ts
app.get(
'/static/*',
serveStatic({
root: './',
rewriteRequestPath: (path) =>
path.replace(/^\/static/, '/statics'),
})
)
```
### `mimes`
MIME タイプを追加するためには `mimes` を使用します:
```ts
app.get(
'/static/*',
serveStatic({
mimes: {
m3u8: 'application/vnd.apple.mpegurl',
ts: 'video/mp2t',
},
})
)
```
### `onFound`
要求されたファイルが見つかったときの処理を `onFound` で指定できます:
```ts
app.get(
'/static/*',
serveStatic({
// ...
onFound: (_path, c) => {
c.header('Cache-Control', `public, immutable, max-age=31536000`)
},
})
)
```
### `onNotFound`
`onNotFound` を使用して、要求されたファイルが見つからない場合の処理を記述できます:
```ts
app.get(
'/static/*',
serveStatic({
onNotFound: (path, c) => {
console.log(`${path} is not found, you access ${c.req.path}`)
},
})
)
```
### `precompressed`
`precompressed` オプションを使うと `Accept-Encoding` ヘッダに基づいて `.br` や `.gz` といった拡張子を持っているファイルが有るか確認し、提供します。 Brotli 、 Zstd 、 Gzip の順で優先されます。 それらが無ければ元のファイルが提供されます。
```ts
app.get(
'/static/*',
serveStatic({
precompressed: true,
})
)
```
## Deno Deploy
Deno Deploy は、クラウド上で JavaScript や TypeScript アプリケーションを実行するサーバレスプラットフォームです。
GitHub のデプロイと同様に、連携機能を通してアプリケーションをデプロイしたり実行したりする管理機能を提供します。
Hono は Deno Deploy もサポートしています。 [公式ドキュメント](https://docs.deno.com/deploy/manual/)を参照してください。
## テスト
Deno でアプリケーションをテストするのは簡単です。
`Deno.test` と、公式ライブラリの `assert` か `assertEquals` を [@std/assert](https://jsr.io/@std/assert) からインポートして書いてください。
```sh
deno add jsr:@std/assert
```
```ts [hello.ts]
import { Hono } from 'hono'
import { assertEquals } from '@std/assert'
Deno.test('Hello World', async () => {
const app = new Hono()
app.get('/', (c) => c.text('Please test me'))
const res = await app.request('http://localhost/')
assertEquals(res.status, 200)
})
```
次にこのコマンドを実行します:
```sh
deno test hello.ts
```
## npm と JSR
Hono は、(JavaScript レジストリである) [npm](https://www.npmjs.com/package/hono) と [JSR](https://jsr.io/@hono/hono) のどちらも使用可能です。 `deno.json` 内で `npm:hono` または `jsr:@hono/hono` のどちらかを使用することができます:
```json
{
"imports": {
"hono": "jsr:@hono/hono" // [!code --]
"hono": "npm:hono" // [!code ++]
}
}
```
ミドルウェアを使用するために、 import で [Deno directory](https://docs.deno.com/runtime/fundamentals/configuration/#custom-path-mappings) 構文を使用する必要があります。
```json
{
"imports": {
"hono/": "npm:/hono/"
}
}
```
サードパーティ製のミドルウェアを使用する際に、適切な TypeScript の型インタフェースを使用するミドルウェアとして同じレジストリの Hono を使用する必要があるかもしれません。 たとえば、 npm からミドルウェアを使用している場合、 Hono も npm から使用スべきです:
```json
{
"imports": {
"hono": "npm:hono",
"zod": "npm:zod",
"@hono/zod-validator": "npm:@hono/zod-validator"
}
}
```
[JSR](https://jsr.io/@hono) で多くのサードパーティ製ミドルウェアパッケージを提供しています。 JSR でミドルウェアを使用する際は、 Hono も JSR から使用してください:
```json
{
"imports": {
"hono": "jsr:@hono/hono",
"zod": "npm:zod",
"@hono/zod-validator": "jsr:@hono/zod-validator"
}
}
```
# Fastly Compute
[Fastly Compute](https://www.fastly.com/products/edge-compute) は好きな言語で書かれたコードをグローバルネットワーク上で実行できる高度なエッジコンピューティングシステムです。 Hono はもちろん Fastly Compute 上でも動作します。
[Fastly CLI](https://www.fastly.com/documentation/reference/tools/cli/) を使用すると、少しのコマンドでアプリケーションをローカルで開発して、公開できます。
## 1. Setup
スターターは Fastly Compute でも使用できます。
"create-hono" コマンドでプロジェクトを開始しましょう。
`fastly` テンプレートを選択します。
::: code-group
```sh [npm]
npm create hono@latest my-app
```
```sh [yarn]
yarn create hono my-app
```
```sh [pnpm]
pnpm create hono my-app
```
```sh [bun]
bun create hono@latest my-app
```
```sh [deno]
deno init --npm hono my-app
```
:::
`my-app` に移動して依存関係をインストールします。
::: code-group
```sh [npm]
cd my-app
npm i
```
```sh [yarn]
cd my-app
yarn
```
```sh [pnpm]
cd my-app
pnpm i
```
```sh [bun]
cd my-app
bun i
```
:::
## 2. Hello World
`src/index.ts` を変更します:
```ts
// src/index.ts
import { Hono } from 'hono'
import { fire } from '@fastly/hono-fastly-compute'
const app = new Hono()
app.get('/', (c) => c.text('Hello Fastly!'))
fire(app)
```
> [!NOTE]
> アプリケーションのトップレベルで、 `@fastly/hono-fastly-compute'` から `fire` (または `buildFire()`) を使用する際に、 `'hono/quick'` よりはむしろ `'hono'` から `Hono` を使用する方が適切です。 `fire` を使用すると、アプリケーションの初期化段階でルータが内部データを構築してしまうためです。
## 3. Run
ローカルで開発サーバーを起動し、ブラウザで `http://localhost:7676` にアクセスしてください。
::: code-group
```sh [npm]
npm run start
```
```sh [yarn]
yarn start
```
```sh [pnpm]
pnpm run start
```
```sh [bun]
bun run start
```
:::
## 4. デプロイ
ビルドして、あなたの Fastly アカウントにデプロイするには以下のコマンドを実行します。 あなたがアプリケーションを始めてデプロイする場合、アカウントに新しいサービスを作成するように求められます。
まだアカウントを持っていない場合は、[作成する必要があります](https://www.fastly.com/signup/)。
::: code-group
```sh [npm]
npm run deploy
```
```sh [yarn]
yarn deploy
```
```sh [pnpm]
pnpm run deploy
```
```sh [bun]
bun run deploy
```
:::
## バインディング
Fastly Compute では、高速なプラットフォームリソースをバインドすることができます。 たとえば、KV ストア、 Config ストア、 Secret ストア、バックエンド、アクセス制御リスト、名前付きログストリーム、環境変数 などです。 `c.env` を通してアクセスすることができ、個々の SDK の型があります。
これらのバインディングを使用するには、 `@fastly/hono-fastly-compute` から `fire` の代わりに `buildFire` を import します。 [bindings](https://github.com/fastly/compute-js-context?tab=readme-ov-file#typed-bindings-with-buildcontextproxy) を定義して、 [`buildFire()`](https://github.com/fastly/hono-fastly-compute?tab=readme-ov-file#basic-example) に渡して `fire` を取得します。 `Hono` を構築する際に、 `Env` 型を定義するために `fire.Bindings` を使用してください。
```ts
// src/index.ts
import { buildFire } from '@fastly/hono-fastly-compute'
const fire = buildFire({
siteData: 'KVStore:site-data', // "site-data" という名前の KV ストアがあります
})
const app = new Hono<{ Bindings: typeof fire.Bindings }>()
app.put('/upload/:key', async (c, next) => {
// 例. KV ストア にアクセスします
const key = c.req.param('key')
await c.env.siteData.put(key, c.req.body)
return c.text(`Put ${key} successfully!`)
})
fire(app)
```
# Google Cloud Run
[Google Cloud Run](https://cloud.google.com/run) は Google Cloud 上に構築されたサーバーレスプラットフォームです。 イベントに対してコードを実行でき、基盤となるコンピューティングリソースは Google が自動で管理します。
Google Cloud Run はサービスを実行するためにコンテナを使用します。 つまり、 Dockerfile を設定することで好きなランタイム (Deno や Bun 等) を使用できることを意味します。 Dockerfile が無かった場合、 Google Cloud Run はデフォルトの Node.js ビルドパックを使用します。
このガイドは、あなたがすでに Google Cloud アカウントと請求先アカウントを持っていることを前提にしています。
## 1. CLI をインストールする
Google Cloud Platform での作業は、 [gcloud CLI](https://cloud.google.com/sdk/docs/install) を使用するのが最も簡単です。
例えば、 MacOS で Homebrew を使用するとき:
```sh
brew install --cask gcloud-cli
```
CLI で認証します。
```sh
gcloud auth login
```
## 2. プロジェクトをセットアップする
プロジェクトを作成します。 提案されるプロジェクト ID をそのまま使用してください。
```sh
gcloud projects create --set-as-default --name="my app"
```
プロジェクト ID とプロジェクト番号を簡単に使用するために環境変数を作成してください。 `gcloud projects list` コマンドでプロジェクトが正しく表示されるまでは最大30秒かかります。
```sh
PROJECT_ID=$(gcloud projects list \
--format='value(projectId)' \
--filter='name="my app"')
PROJECT_NUMBER=$(gcloud projects list \
--format='value(projectNumber)' \
--filter='name="my app"')
echo $PROJECT_ID $PROJECT_NUMBER
```
請求先アカウント ID を探します。
```sh
gcloud billing accounts list
```
前のコマンドの出力から、請求先アカウントをプロジェクトに追加します。
```sh
gcloud billing projects link $PROJECT_ID \
--billing-account=[billing_account_id]
```
必要な API を有効化します。
```sh
gcloud services enable run.googleapis.com \
cloudbuild.googleapis.com
```
Cloud Build にアクセスできるように、サービスアカウントの権限を更新します。
```sh
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member=serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
--role=roles/run.builder
```
## 3. Hello World
"create-hono" コマンドでプロジェクトを始めましょう! `nodejs` を選択してください。
```sh
npm create hono@latest my-app
```
`my-app` ディレクトリに移動し、依存関係をインストールします。
```sh
cd my-app
npm i
```
`src/index.ts` で ポートを `8080` に設定します。
```ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
serve({
fetch: app.fetch,
port: 3000 // [!code --]
port: 8080 // [!code ++]
}, (info) => {
console.log(`Server is running on http://localhost:${info.port}`)
})
```
ローカルで開発サーバーを実行し、 Web ブラウザで http://localhost:8080 にアクセスしてください。
```sh
npm run dev
```
## 4. デプロイ
コマンドでデプロイを開始し、対話形式のプロンプトに従って設定をします。 (例: リージョンの設定)
```sh
gcloud run deploy my-app --source . --allow-unauthenticated
```
## ランタイムを変更する
Deno や Bun (または、カスタム Node.js コンテナ) を使用してデプロイを行いたい場合、 `Dockerfile` (と、必要に応じて `.dockerignore` ) を使用したい環境に合わせて設定してください。
コンテナ化の詳細は、以下のドキュメントを参照してください:
- [Node.js](/docs/getting-started/nodejs#building-deployment)
- [Bun](https://bun.com/guides/ecosystem/docker)
- [Deno](https://docs.deno.com/examples/google_cloud_run_tutorial)
# Lambda@Edge
[Lambda@Edge](https://aws.amazon.com/lambda/edge/) は Amazon Web Services のサーバーレスプラットフォームです。 これにより、 Amazon CloudFront のエッジロケーションで Lambda 関数を実行できるようになり、 HTTP リクエスト/レスポンスをカスタマイズできるようになります。
Hono は Node.js 18 以上の Lambda@Edge をサポートします。
## 1. セットアップ
Lambda@Edge でアプリケーションを作成する時に
[CDK](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-cdk.html)
は CloudFront や IAM ロール、 API ゲートウェイなどの機能を設定するのに役立ちます。
`cdk` CLI を使用してプロジェクトを初期化します。
::: code-group
```sh [npm]
mkdir my-app
cd my-app
cdk init app -l typescript
npm i hono @hono/lambda-edge
mkdir lambda
```
```sh [yarn]
mkdir my-app
cd my-app
cdk init app -l typescript
yarn add hono @hono/lambda-edge
mkdir lambda
```
```sh [pnpm]
mkdir my-app
cd my-app
cdk init app -l typescript
pnpm add hono @hono/lambda-edge
mkdir lambda
```
```sh [bun]
mkdir my-app
cd my-app
cdk init app -l typescript
bun add hono @hono/lambda-edge
mkdir lambda
```
:::
## 2. Hello World
`lambda/index_edge.ts` を編集します。
```ts
import { Hono } from 'hono'
import { handle } from '@hono/lambda-edge'
const app = new Hono()
app.get('/', (c) => c.text('Hello Hono on Lambda@Edge!'))
export const handler = handle(app)
```
## 3. デプロイ
`bin/my-app.ts` を変更します。
```ts
#!/usr/bin/env node
import 'source-map-support/register'
import * as cdk from 'aws-cdk-lib'
import { MyAppStack } from '../lib/my-app-stack'
const app = new cdk.App()
new MyAppStack(app, 'MyAppStack', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: 'us-east-1',
},
})
```
`lambda/cdk-stack.ts` も変更します。
```ts
import { Construct } from 'constructs'
import * as cdk from 'aws-cdk-lib'
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'
import * as lambda from 'aws-cdk-lib/aws-lambda'
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'
import * as s3 from 'aws-cdk-lib/aws-s3'
export class MyAppStack extends cdk.Stack {
public readonly edgeFn: lambda.Function
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props)
const edgeFn = new NodejsFunction(this, 'edgeViewer', {
entry: 'lambda/index_edge.ts',
handler: 'handler',
runtime: lambda.Runtime.NODEJS_20_X,
})
// Upload any html
const originBucket = new s3.Bucket(this, 'originBucket')
new cloudfront.Distribution(this, 'Cdn', {
defaultBehavior: {
origin: new origins.S3Origin(originBucket),
edgeLambdas: [
{
functionVersion: edgeFn.currentVersion,
eventType: cloudfront.LambdaEdgeEventType.VIEWER_REQUEST,
},
],
},
})
}
}
```
最後に、以下のコマンドを実行してデプロイします:
```sh
cdk deploy
```
## コールバック
Basic 認証後にリクエストを処理したい場合は `c.env.callback()` を使用できます。
```ts
import { Hono } from 'hono'
import { basicAuth } from 'hono/basic-auth'
import type { Callback, CloudFrontRequest } from '@hono/lambda-edge'
import { handle } from '@hono/lambda-edge'
type Bindings = {
callback: Callback
request: CloudFrontRequest
}
const app = new Hono<{ Bindings: Bindings }>()
app.get(
'*',
basicAuth({
username: 'hono',
password: 'acoolproject',
})
)
app.get('/', async (c, next) => {
await next()
c.env.callback(null, c.env.request)
})
export const handler = handle(app)
```
# Netlify
Netlify は静的サイトホスティングとサーバーレスバックエンドサービスを提供します。 [Edge Functions](https://docs.netlify.com/edge-functions/overview/) を使用すると Web ページを動的にすることが出来ます。
Edge Functions は Deno と TypeScript をサポートしており、 [Netlify CLI](https://docs.netlify.com/cli/get-started/) を使用することで簡単にデプロイ出来ます。 Hono を使用して Netlify Edge Functions 向けのアプリケーションを作成できます。
## 1. セットアップ
Netlify 向けのスターターももちろん用意されています。
"create-hono" コマンドでプロジェクトを始めましょう。
この例では `netlify` テンプレートを選びます。
::: code-group
```sh [npm]
npm create hono@latest my-app
```
```sh [yarn]
yarn create hono my-app
```
```sh [pnpm]
pnpm create hono my-app
```
```sh [bun]
bun create hono@latest my-app
```
```sh [deno]
deno init --npm hono my-app
```
:::
`my-app` に移動します。
## 2. Hello World
`netlify/edge-functions/index.ts` を変更します:
```ts
import { Hono } from 'jsr:@hono/hono'
import { handle } from 'jsr:@hono/netlify'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export default handle(app)
```
## 3. Run
Netlify CLI で開発サーバーを起動し、 Web ブラウザで `http://localhost:8888` にアクセスします。
```sh
netlify dev
```
## 4. デプロイ
`netlify deploy` コマンドでデプロイ出来ます。
```sh
netlify deploy --prod
```
## `Context`
Netlify 向けの `Context` は `c.env` を使用できます:
```ts
import { Hono } from 'jsr:@hono/hono'
import { handle } from 'jsr:@hono/netlify'
// Import the type definition
import type { Context } from 'https://edge.netlify.com/'
export type Env = {
Bindings: {
context: Context
}
}
const app = new Hono()
app.get('/country', (c) =>
c.json({
'You are in': c.env.context.geo.country?.name,
})
)
export default handle(app)
```
# Next.js
Next.js は、フレキシブルな React フレームワークで、高速な Web アプリケーションを構築するための構成要素を提供します。
Node.js ランタイムを使用している場合、 Next.js 上で Hono を実行することができます。 \
Vercel 上では、 Vercel Functions を使用することで Next.js と一緒に Hono をデプロイすることが簡単にできます。
## 1. セットアップ
Next.js 用のスターターが使用可能です。
"create-hono" コマンドでプロジェクトを開始します。
この例では、 `nextjs` テンプレートを選択します。
::: code-group
```sh [npm]
npm create hono@latest my-app
```
```sh [yarn]
yarn create hono my-app
```
```sh [pnpm]
pnpm create hono my-app
```
```sh [bun]
bun create hono@latest my-app
```
```sh [deno]
deno init --npm hono my-app
```
:::
`my-app` フォルダに移動し、依存関係をインストールします。
::: code-group
```sh [npm]
cd my-app
npm i
```
```sh [yarn]
cd my-app
yarn
```
```sh [pnpm]
cd my-app
pnpm i
```
```sh [bun]
cd my-app
bun i
```
:::
## 2. Hello World
App ルータを使用している場合、 `app/api/[[...route]]/route.ts` を編集します。 他のオプションについては、 [Supported HTTP Methods](https://nextjs.org/docs/app/building-your-application/routing/route-handlers#supported-http-methods) セクションを参照してください。
```ts
import { Hono } from 'hono'
import { handle } from '@hono/vercel'
const app = new Hono().basePath('/api')
app.get('/hello', (c) => {
return c.json({
message: 'Hello Next.js!',
})
})
export const GET = handle(app)
export const POST = handle(app)
```
`handle` comes from the `@hono/vercel` package, which the starter installs. On an existing project, add it with `npm i @hono/vercel`.
## 3. Run
ローカルで開発サーバを実行し、ウェブブラウザで `http://localhost:3000` にアクセスします。
::: code-group
```sh [npm]
npm run dev
```
```sh [yarn]
yarn dev
```
```sh [pnpm]
pnpm dev
```
```sh [bun]
bun run dev
```
:::
`/api/hello` にアクセスすると、 JSON を返します。 しかし、 React UI を構築している場合、 Hono でフルスタックアプリケーションを作成することができます。
## 4. デプロイ
Vercel のアカウントがある場合、 Git リポジトリをリンクすることでデプロイすることができます。
## Pages Router
Pages ルータを使用している場合、まず Node.js アダプタをインストールする必要があります。
::: code-group
```sh [npm]
npm i @hono/node-server
```
```sh [yarn]
yarn add @hono/node-server
```
```sh [pnpm]
pnpm add @hono/node-server
```
```sh [bun]
bun add @hono/node-server
```
:::
`pages/api/[[...route]].ts` 内で `@hono/node-server` からインポートした `getRequestListener` 関数を利用することができます。
```ts
import { getRequestListener } from '@hono/node-server'
import { Hono } from 'hono'
import type { PageConfig } from 'next'
export const config: PageConfig = {
api: {
bodyParser: false,
},
}
const app = new Hono().basePath('/api')
app.get('/hello', (c) => {
return c.json({
message: 'Hello Next.js!',
})
})
export default getRequestListener(app.fetch)
```
Pages ルータを動作させるには、プロジェクトのダッシュボードの環境変数か `.env` ファイルで Vercel Node.js ヘルパーを無効に設定することが重要です。
```text
NODEJS_HELPERS=0
```
# Node.js
[Node.js](https://nodejs.org/) はオープンソースでクロスプラットフォームの JavaScript ランタイム環境です。
Hono は Node.js 向けに設計されたわけではありませんが、 [Node.js Adapter](https://github.com/honojs/node-server) を使うと Node.js でも実行できます。
::: info
Node.js 18.x 以上で動作します。 具体的に必要な Node.js のバージョンは以下の通りです:
- 18.x => 18.14.1+
- 19.x => 19.7.0+
- 20.x => 20.0.0+
具体的には、各メジャーリリースの最新バージョンを使用するだけです。
:::
## 1. セットアップ
Node.js のスターターが利用可能です。
"create-hono" コマンドでプロジェクトを開始してください。
この例では `nodejs` テンプレートを選択してください。
::: code-group
```sh [npm]
npm create hono@latest my-app
```
```sh [yarn]
yarn create hono my-app
```
```sh [pnpm]
pnpm create hono my-app
```
```sh [bun]
bun create hono@latest my-app
```
```sh [deno]
deno init --npm hono my-app
```
:::
`my-app` に移動して依存パッケージをインストールします。
::: code-group
```sh [npm]
cd my-app
npm i
```
```sh [yarn]
cd my-app
yarn
```
```sh [pnpm]
cd my-app
pnpm i
```
```sh [bun]
cd my-app
bun i
```
:::
## 2. Hello World
`src/index.ts` を編集します:
```ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Node.js!'))
serve(app)
```
サーバをグレースフルシャットダウンしたい場合は、次のように記述します:
```ts
const server = serve(app)
// graceful shutdown
process.on('SIGINT', () => {
server.close()
process.exit(0)
})
process.on('SIGTERM', () => {
server.close((err) => {
if (err) {
console.error(err)
process.exit(1)
}
process.exit(0)
})
})
```
::: info
Node.js では、`serve()` は `node:http` モジュールをラップし、基盤となるサーバーインスタンスを返すため、閉じるのはあなた次第です。 Bun と Deno は Fetch ハンドラーをネイティブに提供し、サーバー自身を管理するため、それらのガイドにはこの手順は含まれていません。
:::
## 3. 実行
開発サーバーをローカルで起動し、 Web ブラウザで `http://localhost:3000` にアクセスします。
::: code-group
```sh [npm]
npm run dev
```
```sh [yarn]
yarn dev
```
```sh [pnpm]
pnpm dev
```
:::
## ポート番号の変更
`port` オプションでポート番号を指定できます。
```ts
serve({
fetch: app.fetch,
port: 8787,
})
```
## WebSocket
WebSocket サポートは `@hono/node-server` に組み込まれています。 `ws` をインストールし、 TypeScript を使用している場合は `@types/ws` もインストールします。 次に、 `{ noServer: true }` を指定して `WebSocketServer` を生成し、 `websocket` オプションを使用して `serve()` に渡します。
`@hono/node-ws` は非推奨です。
```ts
import { serve, upgradeWebSocket } from '@hono/node-server'
import { Hono } from 'hono'
import { WebSocketServer } from 'ws'
const app = new Hono()
app.get(
'/ws',
upgradeWebSocket(() => ({
onMessage(event, ws) {
ws.send(event.data)
},
}))
)
const wss = new WebSocketServer({ noServer: true })
serve({
fetch: app.fetch,
websocket: { server: wss },
})
```
## 生の Node.js API へのアクセス
Node.js API は `c.env.incoming` と `c.env.outgoing` からアクセスできます。
```ts
import { Hono } from 'hono'
import { serve, type HttpBindings } from '@hono/node-server'
// or `Http2Bindings` if you use HTTP2
type Bindings = HttpBindings & {
/* ... */
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/', (c) => {
return c.json({
remoteAddress: c.env.incoming.socket.remoteAddress,
})
})
serve(app)
```
## 静的ファイルの配信
`serveStatic` を使うことでローカルファイルシステムから静的ファイルを配信できます。 例えば、ディレクトリ構造が次のような場合を考えます:
```sh
./
├── favicon.ico
├── index.ts
└── static
├── hello.txt
└── image.png
```
パス `/static/*` へのリクエストが来て、 `./static` 配下のファイルを返したい場合、次のように記述できます:
```ts
import { serveStatic } from '@hono/node-server/serve-static'
app.use('/static/*', serveStatic({ root: './' }))
```
::: warning
`root` オプションは、カレントの作業ディレクトリ (`process.cwd()`) からの相対パスで解決します。 これは、サーバを実行している場所ではなく、 **ソースファイルが置かれている場所によって動作が異なる** ことを意味します。 異なるディレクトリからサーバを起動した場合、ファイル解決が失敗する可能性があります。
ソースファイルと常に同じディレクトリを指す信頼できるパス解決を行うには、 `import.meta.url` を使用してください:
```ts
import { fileURLToPath } from 'node:url'
import { serveStatic } from '@hono/node-server/serve-static'
app.use(
'/static/*',
serveStatic({ root: fileURLToPath(new URL('./', import.meta.url)) })
)
```
:::
ディレクトリルートの `favicon.ico` を配信するには `path` オプションを使用します:
```ts
app.use('/favicon.ico', serveStatic({ path: './favicon.ico' }))
```
パス `/hello.txt` や `/image.png` へのリクエストが来て、 `./static/hello.txt` や `./static/image.png` という名前のファイルを返したい場合、次のように使用できます:
```ts
app.use('*', serveStatic({ root: './static' }))
```
::: warning
ルートのマッチングは大文字小文字を区別しますが、ファイルシステムはそうではない場合があります。 Windows と macOS では、`/ADMIN/secret.txt` は `/admin/secret.txt` と同じファイルとして解決され、Windows は `/PROTEC~1` のような 8.3 形式の短縮名も受け付けます。 `app.use('/admin/*', auth)` のようなパススコープのミドルウェアはそれらの表記にマッチしないため、静的ルート配下のファイルを保護するためにそれらに頼らないでください。 保護するファイルは配信するディレクトリの外に置くか、`serveStatic` が扱うすべてのパスにミドルウェアを適用してください。
:::
### `rewriteRequestPath`
`http://localhost:3000/static/*` を `./statics` にマップしたい場合、 `rewriteRequestPath` オプションを使用できます:
```ts
app.get(
'/static/*',
serveStatic({
root: './',
rewriteRequestPath: (path) =>
path.replace(/^\/static/, '/statics'),
})
)
```
## http2
Hono を [Node.js http2 Server](https://nodejs.org/api/http2.html) でも実行できます。
### 暗号化されていない http2
```ts
import { createServer } from 'node:http2'
const server = serve({
fetch: app.fetch,
createServer,
})
```
### 暗号化された http2
```ts
import { createSecureServer } from 'node:http2'
import { readFileSync } from 'node:fs'
const server = serve({
fetch: app.fetch,
createServer: createSecureServer,
serverOptions: {
key: readFileSync('localhost-privkey.pem'),
cert: readFileSync('localhost-cert.pem'),
},
})
```
## ビルドとデプロイ
::: code-group
```sh [npm]
npm run build
```
```sh [yarn]
yarn run build
```
```sh [pnpm]
pnpm run build
```
```sh [bun]
bun run build
```
::: info
フロントエンドのフレームワークをもつアプリケーションは [Hono's Vite plugins](https://github.com/honojs/vite-plugins) を使用する必要があるかもしれません。
:::
### Dockerfile
Node.js の Dockerfile の例は次の通りです。
```Dockerfile
FROM node:22-alpine AS base
FROM base AS builder
RUN apk add --no-cache gcompat
WORKDIR /app
COPY package*json tsconfig.json src ./
RUN npm ci && \
npm run build && \
npm prune --production
FROM base AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 hono
COPY --from=builder --chown=hono:nodejs /app/node_modules /app/node_modules
COPY --from=builder --chown=hono:nodejs /app/dist /app/dist
COPY --from=builder --chown=hono:nodejs /app/package.json /app/package.json
USER hono
EXPOSE 3000
CMD ["node", "/app/dist/index.js"]
```
# Service Worker
[Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) はキャッシュやプッシュ通知などを処理するために Web ブラウザのバックグラウンドで動くスクリプトです。 Service Worker アダプタを使用することで、 Hono で作ったアプリケーションを [FetchEvent](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent) ハンドラとしてブラウザ内で動かすことができます。
このページでは [Vite](https://vitejs.dev/) を使ってプロジェクトを作る例を解説します。
## 1. セットアップ
まずは、プロジェクトのディレクトリを作って移動します:
```sh
mkdir my-app
cd my-app
```
プロジェクトに必要なファイルを作成します。 以下のように `package.json` を書いてください:
```json
{
"name": "my-app",
"private": true,
"scripts": {
"dev": "vite dev"
},
"type": "module"
}
```
同様に、 `tsconfig.json` も作ってください:
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "WebWorker"],
"moduleResolution": "bundler"
},
"include": ["./"],
"exclude": ["node_modules"]
}
```
次に、必要なパッケージをインストールします。
::: code-group
```sh [npm]
npm i hono @hono/service-worker
npm i -D vite
```
```sh [yarn]
yarn add hono @hono/service-worker
yarn add -D vite
```
```sh [pnpm]
pnpm add hono @hono/service-worker
pnpm add -D vite
```
```sh [bun]
bun add hono @hono/service-worker
bun add -D vite
```
:::
## 2. Hello World
`index.html` を書きます:
```html
Hello World by Service Worker
```
`main.ts` は Service Worker を登録するためのスクリプトです:
```ts
function register() {
navigator.serviceWorker
.register('/sw.ts', { scope: '/sw', type: 'module' })
.then(
function (_registration) {
console.log('Register Service Worker: Success')
},
function (_error) {
console.log('Register Service Worker: Error')
}
)
}
function start() {
navigator.serviceWorker
.getRegistrations()
.then(function (registrations) {
for (const registration of registrations) {
console.log('Unregister Service Worker')
registration.unregister()
}
register()
})
}
start()
```
`sw.ts` で、 Hono を使ってアプリケーションを作り、 Service Worker アダプタの `handle` 関数を使って `fetch` イベントに登録します。 これにより、 Hono アプリケーションが `/sw` 以下のアクセスを傍受できるようになります。
```ts
// To support types
// https://github.com/microsoft/TypeScript/issues/14877
declare const self: ServiceWorkerGlobalScope
import { Hono } from 'hono'
import { handle } from '@hono/service-worker'
const app = new Hono().basePath('/sw')
app.get('/', (c) => c.text('Hello World'))
self.addEventListener('fetch', handle(app))
```
### `fire()` を使う
`fire()` 関数は自動で `addEventListener('fetch', handle(app))` を呼び、 コードをより簡潔にします。
```ts
import { Hono } from 'hono'
import { fire } from '@hono/service-worker'
const app = new Hono().basePath('/sw')
app.get('/', (c) => c.text('Hello World'))
fire(app)
```
## 3. 実行
開発サーバーを起動します
::: code-group
```sh [npm]
npm run dev
```
```sh [yarn]
yarn dev
```
```sh [pnpm]
pnpm run dev
```
```sh [bun]
bun run dev
```
:::
デフォルトでは、開発サーバーは `5173` 番ポートで起動します。 ブラウザで `http://localhost:5173/` にアクセスすることで Service Worker の登録が完了します。 次に、 `/sw` にアクセスして Hono アプリケーションからのレスポンスを見ることができます。
# Supabase Edge Functions
[Supabase](https://supabase.com/) はオープンソースの Firebase 代替で、データベース、認証、ストレージ、今回使うサーバーレス関数などの Firebase の機能と同様なツールスイートを提供します。
Supabase Edge Functions はグローバルに分散されたサーバーサイド TypeScript 関数で、高いパフォーマンスのためにユーザーの近くで実行されます。 [Deno](https://deno.com/) を用いており、セキュリティの向上やモダン JavaScript/TypeScript などのメリットがあります。
Supabase Edge Functions を始める方法は以下の通りです:
## 1. セットアップ
### 前提条件
始める前に、 Supabase CLI がインストールされていることを確認してください。 まだインストールしていない場合は[公式ドキュメント](https://supabase.com/docs/guides/cli/getting-started)にしたがってインストールしてください。
### 新しいプロジェクトを作成する
1. ターミナル/コマンドプロンプトを開いてください。
2. 次のコマンドを実行して、ローカルマシンのディレクトリに新しい Supabase プロジェクトを作成します。
```bash
supabase init
```
このコマンドは現在のディレクトリに新しい Supabase プロジェクトを初期化します。
### エッジ関数の追加
3. Supabase プロジェクトの中で新しいエッジ関数を `hello-world` という名前で作成します:
```bash
supabase functions new hello-world
```
このコマンドで名前を指定して新しいエッジ関数をプロジェクトに作成します。
## 2. Hello World
`supabase/functions/hello-world/index.ts` にある `hello-world` 関数を編集してください:
```ts
import { Hono } from 'jsr:@hono/hono'
// change this to your function name
const functionName = 'hello-world'
const app = new Hono().basePath(`/${functionName}`)
app.get('/hello', (c) => c.text('Hello from hono-server!'))
Deno.serve(app.fetch)
```
## 3. Run
ローカルで関数を動かすには次のコマンドを実行します:
1. 以下のコマンドを実行して関数を開始します:
```bash
supabase start # start the supabase stack
supabase functions serve --no-verify-jwt # start the Functions watcher
```
`--no-verify-jwt` フラグはローカル開発中に JWT 検証をバイパスできます。
2. cURL や Postman を使用して `http://127.0.0.1:54321/functions/v1/hello-world/hello` に GET リクエストを送信します:
```bash
curl --location 'http://127.0.0.1:54321/functions/v1/hello-world/hello'
```
"Hello from hono-server!" という文字列が返されるはずです。
## 4. デプロイ
全てのエッジ関数をひとつのコマンドで Supabase にデプロイ出来ます:
```bash
supabase functions deploy
```
もしくは deploy コマンドで関数名を指定してそれぞれのエッジ関数をデプロイ出来ます:
```bash
supabase functions deploy hello-world
```
その他のデプロイ方法については Supabase ドキュメント [Deploying to Production](https://supabase.com/docs/guides/functions/deploy) をお読みください。
# Vercel
Vercel は、 AI クラウドであり、より高速でパーソナライズされたウェブを構築し、拡張し、安全にする開発ツールやクラウドインフラを提供しています。
Hono は設定なしで Vercel にデプロイすることができます。
## 1. セットアップ
Vercel 向けのスターターもあります。
"create-hono" コマンドで始めましょう。
`vercel` テンプレートを選択します。
::: code-group
```sh [npm]
npm create hono@latest my-app
```
```sh [yarn]
yarn create hono my-app
```
```sh [pnpm]
pnpm create hono my-app
```
```sh [bun]
bun create hono@latest my-app
```
```sh [deno]
deno init --npm hono my-app
```
:::
`my-app` に移動し、依存関係をインストールします。
::: code-group
```sh [npm]
cd my-app
npm i
```
```sh [yarn]
cd my-app
yarn
```
```sh [pnpm]
cd my-app
pnpm i
```
```sh [bun]
cd my-app
bun i
```
:::
次のステップでは、ローカル環境のアプリケーション上で動作する Vercel CLI を使用します。 まだインストールされていない場合は、 [Vercel CLI ドキュメント](https://vercel.com/docs/cli) に従ってグローバルにインストールしてください。
## 2. Hello World
プロジェクトの `index.ts` あるいは `src/index.ts` 内で、 default export として Hono アプリケーションをエクスポートします。
```ts
import { Hono } from 'hono'
const app = new Hono()
const welcomeStrings = [
'Hello Hono!',
'To learn more about Hono on Vercel, visit https://vercel.com/docs/frameworks/backend/hono',
]
app.get('/', (c) => {
return c.text(welcomeStrings.join('\n\n'))
})
export default app
```
`vercel` テンプレートを使用している場合は、すでにセットアップされています。
## 3. Run
ローカルで開発サーバを実行するには:
```sh
vercel dev
```
`localhost:3000` にアクセスするとテキストレスポンスを返します。
## 4. デプロイ
`vc deploy` を使用して Vercel にデプロイします。
```sh
vercel deploy
```
## 参考
[Vercel ドキュメントで Hono について学ぶ](https://vercel.com/docs/frameworks/backend/hono).
# WebAssembly (w/ WASI)
[WebAssembly][wasm-core] は、安全なサンドボックス化されたポータブルなランタイムです。 ウェブブラウザの内部でも外部でも動作します。
実際に:
- (JavaScript のような) 言語は、 WebAssembly (`.wasm` ファイル) に _コンパイルする_
- ([`wasmtime`][wasmtime] や [`jco`][jco] のような) WebAssembly ランタイムは、 WebAssembly バイナリを _実行_ することができる
WebAssembly のコアは、ローカルのファイルシステムやソケットなどにアクセス _しない_ ですが、 [WebAssembly System Interface][wasi] が、
WebAssembly のワークロード配下のプラットフォームを定義できる役割を担っています。
WASI _を持っている_ WebAssembly は、ファイルやソケットなどを操作することができるということです。
::: info
WASI インタフェースをご自身で確認してみたいですか? [`wasi:http`][wasi-http] をチェックしてみてください。
:::
JavaScript における WebAssembly w/ WASI のサポートは、 [StarlingMonkey][sm] によって実現されています。
StarlingMonkey と Hono はどちらもウェブ標準を重視しています。 **Hono は、 WASI が有効な WebAssembly エコシステムにおいてすぐに動作します。 **
[sm]: https://github.com/bytecodealliance/StarlingMonkey
[wasm-core]: https://webassembly.org/
[wasi]: https://wasi.dev/
[bca]: https://bytecodealliance.org/
[wasi-http]: https://github.com/WebAssembly/wasi-http
## 1. セットアップ
WebAssembly の JavaScript エコシステムは、簡単に WASI が有効な WebAssembly コンポーネントを構築し始めるためのツールを提供しています:
- [StarlingMonkey][sm] は、 [SpiderMonkey][spidermonkey] のフォークです。 WebAssembly にコンパイルしコンポーネントを有効にします。
- [`componentize-js`][componentize-js] は、 JavaScript ES modules を WebAssembly コンポーネントに変換します。
- [`jco`][jco] はマルチツールで、コンポーネントをビルドし、型を生成し、 Node.js やブラウザのような環境でコンポーネントを実行します。
::: info
WebAssembly オープンなエコシステムがあり、オープンソースです。 コアプロジェクトでは、 [Bytecode Alliance][bca] やそのメンバーによって管理されています。
新しい機能、イシュー、プルリクエストやその他の貢献などは、常に歓迎されます。
:::
WebAssembly 上で動作する Hono についてのスタータはまだ利用可能ではありませんが、他のプロジェクトと同様に WebAssembly を使った Hono プロジェクトを開始することができます:
::: code-group
```sh [npm]
mkdir my-app
cd my-app
npm init
npm i hono
npm i -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
npm i -D rolldown
```
```sh [yarn]
mkdir my-app
cd my-app
npm init
yarn add hono
yarn add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
yarn add -D rolldown
```
```sh [pnpm]
mkdir my-app
cd my-app
pnpm init --init-type module
pnpm add hono
pnpm add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
pnpm add -D rolldown
```
```sh [bun]
mkdir my-app
cd my-app
npm init
bun add hono
bun add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
```
:::
::: info
プロジェクトで ES modules を使用するには、 `package.json` 内で、 `type` が `"module"` にセットされていることを確認してください。
:::
`my-app` フォルダに移動した後、依存関係をインストールし、 TypeScript を初期化します:
::: code-group
```sh [npm]
npm i
npx tsc --init
```
```sh [yarn]
yarn
yarn tsc --init
```
```sh [pnpm]
pnpm i
pnpm exec tsc --init
```
```sh [bun]
bun i
```
:::
基本的な TypeScript の設定ファイル (`tsconfig.json`) を作成したら、次の設定があることを確認してください:
- `compilerOptions.module` が `"nodenext"` にセットされている
`componentize-js` (とそれを再利用する `jco`) は、単一の JavaScript ファイルしかサポートしないため、
バンドルすることが必須ですが、単一のバンドルファイルを生成するために、 [`rolldown`][rolldown] を使用することができます。
次のような Rolldown (`rolldown.config.mjs`) 設定を使用することができます:
```js
import { defineConfig } from 'rolldown'
export default defineConfig({
input: 'src/component.ts',
external: /wasi:.*/,
output: {
file: 'dist/component.js',
format: 'esm',
},
})
```
::: info
(`rolldown`, `esbuild`, `rollup` など) 使い慣れている他のバンドラを自由に使用してください。
:::
[jco]: https://github.com/bytecodealliance/jco
[componentize-js]: https://github.com/bytecodealliance/componentize-js
[rolldown]: https://rolldown.rs
[spidermonkey]: https://spidermonkey.dev/
## 2. WIT インタフェースや依存関係をセットアップする
[WebAssembly Interface Types (WIT)][wit] は、インタフェース定義言語 ("IDL") です。 WebAssembly コンポーネントが、どの機能を使用するか ("imports") や何を提供するか ("exports") を管理します。
標準化された WIT インタフェースのうち、 [`wasi:http`][wasi-http] は HTTP リクエスト (リクエストを受け取るかまたはリクエストを送るか) を扱うためのものです。 ウェブサーバを作成するにあたり、コンポーネントは、 [WIT world][wit-world] において `wasi:http/incoming-handler` を使用することを宣言しなければなりません。
まず初めに、 `wit/component.wit` という名前のファイルで、コンポーネントの WIT world をセットアップします:
```txt
package example:hono;
world component {
export wasi:http/incoming-handler@0.2.6;
}
```
簡単に言うと、上記の WIT ファイルは、コンポーネントが HTTP リクエストを "受信する"/"処理する" という機能を提供することを意味しています。
`wasi:http/incoming-handler` インタフェースは、上流の標準化された WIT インタフェース (リクエストの構造についての仕様など) に依存しています。
サードパーティ製 (Bytecode Alliance がメンテナンスしている) の WIT インタフェースを取得するために、利用できるツールの1つは [`wkg`][wkg] になります:
```sh
wkg wit fetch
```
`wkg` は実行が終わると、 `wit` フォルダ内に `component.wit` と並んで、新しい `deps` フォルダが作成されていることがわかります:
```
wit
├── component.wit
└── deps
├── wasi-cli-0.2.6
│ └── package.wit
├── wasi-clocks-0.2.6
│ └── package.wit
├── wasi-http-0.2.6
│ └── package.wit
├── wasi-io-0.2.6
│ └── package.wit
└── wasi-random-0.2.6
└── package.wit
```
[wkg]: https://github.com/bytecodealliance/wasm-pkg-tools
[wit-world]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#wit-worlds
[wit]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md
## 3. Hello Wasm
WebAssembly で HTTP サーバをビルドするために、 [`jco-std`][jco-std] プロジェクトを使用することができます。 ヘルパーが含まれていて、標準的な Hono の体験と似たような体験をすることができます。
`src/component.ts` という名前のファイル内に WebAssembly コンポーネントとして基本的な Hono アプリケーションをもつ `component` world を構築してみましょう:
```ts
import { Hono } from 'hono'
import { fire } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'
const app = new Hono()
app.get('/hello', (c) => {
return c.json({ message: 'Hello from WebAssembly!' })
})
fire(app)
// 上記で wasi HTTP が設定されている `fire()` をコールしていますが、
// 実際には `wasi:http/incoming-handler` インタフェースオブジェクトをエクスポートする必要があります
// それは jco や componentize-js が WASI インタフェースにマッチしている ES module のエクスポートを検索しているからです。
export { incomingHandler } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'
```
## 4. ビルド
Rolldown を使用している (また、 TypeScript のコンパイルを処理するように設定されている) ので、ビルドやバンドルするのに使用することができます:
::: code-group
```sh [npm]
npx rolldown -c
```
```sh [yarn]
yarn rolldown -c
```
```sh [pnpm]
pnpm exec rolldown -c
```
```sh [bun]
bun build --target=bun --outfile=dist/component.js ./src/component.ts
```
:::
::: info
バンドルすることが必要です。 WebAssembly JavaScript エコシステムツールは現在のところ単一の JavaScript ファイルしかサポートしておらず、関連したライブラリとともに Hono を含めたいためです。
よりシンプルな要件のコンポーネントの場合、バンドらは必要ありません。
:::
WebAssembly コンポーネントをビルドするために、 `jco` (と間接的に `componentize-js`) を使用します:
::: code-group
```sh [npm]
npx jco componentize -w wit -o dist/component.wasm dist/component.js
```
```sh [yarn]
yarn jco componentize -w wit -o dist/component.wasm dist/component.js
```
```sh [pnpm]
pnpm exec jco componentize -w wit -o dist/component.wasm dist/component.js
```
```sh [bun]
bun run jco componentize -w wit -o dist/component.wasm dist/component.js
```
:::
## 5. Run
Hono WebAssembly HTTP サーバを実行するために、 WASI が使用可能な WebAssembly ランタイムを使用することができます:
- [`wasmtime`][wasmtime]
- `jco` (Node.js で実行)
ここでは、すでにインストールされているので、 `jco serve` を使用します。
::: warning
`jco serve` は開発用であることを示しています。 本番用には推奨されません。
:::
[wasmtime]: https://wasmtime.dev
::: code-group
```sh [npm]
npx jco serve dist/component.wasm
```
```sh [yarn]
yarn jco serve dist/component.wasm
```
```sh [pnpm]
pnpm exec jco serve dist/component.wasm
```
```sh [bun]
bun run jco serve dist/component.wasm
```
:::
次のように出力されるでしょう:
```
$ npx jco serve dist/component.wasm
Server listening @ localhost:8000...
```
`localhost:8000/hello` にリクエストを送ると、 Hono アプリケーションでしていした JSON を出力します。
次のように出力されるでしょう:
```json
{ "message": "Hello from WebAssembly!" }
```
::: info
`jco serve` は、 WebAssembly コンポーネントを基本的な WebAssembly コアモジュールに変換することで動作します。 このため、 Node.js やブラウザのようなランタイム上で動作します。
通常このプロセスは、 `jco transpile` を通して実行され、 WebAssembly コンポーネントのランタイムとして Node.js やブラウザのような JavaScript エンジンを使用することができる方法です。
`jco transpile` がどのように動作するかについては、このガイドの範囲外です。 [The Jco book][jco-book] で詳細について読んでください。
:::
## その他の情報
WASI や WebAssembly コンポーネントなどについて学ぶためには、次のリソースを参照してください:
- [BytecodeAlliance Component Model book][cm-book]
- [`jco` codebase][jco]
- [`jco` example components][jco-example-components] (特に [Hono の例][jco-example-component-hono])
- [Jco book][jco-book]
- [`componentize-js` codebase][componentize-js]
- [StarlingMonkey codebase][sm]
WebAssembly コミュニティに、質問やコメントや貢献、あるいは問題の報告などで連絡を取るには:
- [Bytecode Alliance Zulip](https://bytecodealliance.zulipchat.com) ([#jco チャンネル](https://bytecodealliance.zulipchat.com/#narrow/channel/409526-jco) への投稿がおすすめです)
- [Jco repository](https://github.com/bytecodealliance/jco)
- [componentize-js repository](https://github.com/bytecodealliance/componentize-js)
[cm-book]: https://component-model.bytecodealliance.org/
[jco-book]: https://bytecodealliance.github.io/jco/
[jco-example-components]: https://github.com/bytecodealliance/jco/tree/main/examples/components
[jco-example-component-hono]: https://github.com/bytecodealliance/jco/tree/main/examples/components/http-server-hono
# ベンチマーク
ベンチマークはあくまでもベンチマークですが、非常に重要です。
## ルーター
多くの JavaScript ルーターのベンチマークを行いました。
例えば、 Fastify 内部で使われている `find-my-way` は非常に高速です。
- @medley/router
- find-my-way
- koa-tree-router
- trek-router
- express (includes handling)
- koa-router
まず、それぞれのルーターに以下のルートを登録しました。
それらは現実で実際に使われるものとよく似ています。
```ts twoslash
interface Route {
method: string
path: string
}
// ---cut---
export const routes: Route[] = [
{ method: 'GET', path: '/user' },
{ method: 'GET', path: '/user/comments' },
{ method: 'GET', path: '/user/avatar' },
{ method: 'GET', path: '/user/lookup/username/:username' },
{ method: 'GET', path: '/user/lookup/email/:address' },
{ method: 'GET', path: '/event/:id' },
{ method: 'GET', path: '/event/:id/comments' },
{ method: 'POST', path: '/event/:id/comment' },
{ method: 'GET', path: '/map/:location/events' },
{ method: 'GET', path: '/status' },
{ method: 'GET', path: '/very/deeply/nested/route/hello/there' },
{ method: 'GET', path: '/static/*' },
]
```
そして、以下のようにそれぞれのエンドポイントにリクエストを行いました。
```ts twoslash
interface Route {
method: string
path: string
}
// ---cut---
const routes: (Route & { name: string })[] = [
{
name: 'short static',
method: 'GET',
path: '/user',
},
{
name: 'static with same radix',
method: 'GET',
path: '/user/comments',
},
{
name: 'dynamic route',
method: 'GET',
path: '/user/lookup/username/hey',
},
{
name: 'mixed static dynamic',
method: 'GET',
path: '/event/abcd1234/comments',
},
{
name: 'post',
method: 'POST',
path: '/event/abcd1234/comment',
},
{
name: 'long static',
method: 'GET',
path: '/very/deeply/nested/route/hello/there',
},
{
name: 'wildcard',
method: 'GET',
path: '/static/index.html',
},
]
```
結果を見てみましょう。
### Node.js では...
以下のスクリーンショットは Node.js での結果です。








### Bun では...
以下のスクリーンショットは Bun での結果です。








## Cloudflare Workers
Cloudflare Workers 向けの他のルーターと比較して **Hono は最速です**。
- Machine: Apple MacBook Pro, 32 GiB, M1 Pro
- Scripts: [benchmarks/handle-event](https://github.com/honojs/hono/tree/main/benchmarks/handle-event)
```
Hono x 402,820 ops/sec ±4.78% (80 runs sampled)
itty-router x 212,598 ops/sec ±3.11% (87 runs sampled)
sunder x 297,036 ops/sec ±4.76% (77 runs sampled)
worktop x 197,345 ops/sec ±2.40% (88 runs sampled)
Fastest is Hono
✨ Done in 28.06s.
```
## Deno
Deno の他のフレームワークと比べて **Hono はここでも最速です**。
- Machine: Apple MacBook Pro, 32 GiB, M1 Pro, Deno v1.22.0
- Scripts: [benchmarks/deno](https://github.com/honojs/hono/tree/main/benchmarks/deno)
- Method: `bombardier --fasthttp -d 10s -c 100 'http://localhost:8000/user/lookup/username/foo'`
| Framework | Version | Results |
| --------- | :----------: | -----------------------: |
| **Hono** | 3.0.0 | **Requests/sec: 136112** |
| Fast | 4.0.0-beta.1 | Requests/sec: 103214 |
| Megalo | 0.3.0 | Requests/sec: 64597 |
| Faster | 5.7 | Requests/sec: 54801 |
| oak | 10.5.1 | Requests/sec: 43326 |
| opine | 2.2.0 | Requests/sec: 30700 |
他のベンチマーク結果: [denosaurs/bench](https://github.com/denosaurs/bench)
## Bun
Hono は Bun の最速なフレームワークのひとつです。
以下でご覧いただけます。
- [SaltyAom/bun-http-framework-benchmark](https://github.com/SaltyAom/bun-http-framework-benchmark)
# 開発体験
素晴らしいアプリケーションのためには素晴らしい開発体験が必要です。
幸いなことに、 Cloudflare Workers 、 Deno 、 Bun では TypeScript をトランスパイルすることなく実行できます。
Hono は TypeScript で書かれており型安全なアプリケーションを実現します。
# ミドルウェア
`Response` を返すプリミティブを "ハンドラ" と呼びます。
"Middleware" はハンドラの前後で使用され `Request` と `Response` を処理します。
玉ねぎのような構造です。

例えば、 "X-Response-Time" ヘッダを付与するミドルウェアはこのようになります。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.use(async (c, next) => {
const start = performance.now()
await next()
const end = performance.now()
c.res.headers.set('X-Response-Time', `${end - start}`)
})
```
このシンプルな方法によってカスタムミドルウェアを作成したり、ビルドインミドルウェアやサードパーティーミドルウェアを使用できます。
# 哲学
この章では、 Hono のコンセプトや哲学について説明します。
## 動機
始めは、 Cloudflare Workers でウェブアプリケーションを作りたいだけでした。
しかし、 Cloudflare Workers で動く良いフレームワークがありませんでした。
そのため、 Hono の開発を始めました。
それは、 Trie 木を使ったルーターの作り方を学ぶための良い機会になると思いました。
次に、ある友達が "RegExpRouter" と呼ばれる滅茶苦茶爆速なルーターを持ってやってきました。
また Basic 認証をするミドルウェアを作った友達もいました。
Web 標準の API のみを使うことで、 Deno でも Bun でも動くものを作ることができました。 人々が 「 Bun 向けの Express は無いの? 」 と聞いたとき、 「 いいえ、でも Hono があるよ! 」 と答えることができました。
( Express は現在 Bun でも動きます )
GraphQL サーバーや Firebase 認証、 Sentry ミドルウェアを作ってくれた友達もいます。
また、 Node.js アダプタもあります。
一つのエコシステムが出来上がりました。
言い換えれば、 Hono は超速くて、何でもできて、どこでも動きます。
Hono が **Web 標準の標準** になれることを想像するかもしれません。
# ルーター
ルーターは Hono にとって最も重要な要素です。
Hono は5つのルーターを持ちます。
## RegExpRouter
**RegExpRouter** は JavaScript で最速のルーターです。
これは "RegExp" と呼ばれますが、 Express のような [path-to-regexp](https://github.com/pillarjs/path-to-regexp) を使用する実装ではありません。
それらは線形ループを使用します。
そのため、全てのルートに対して正規表現が照合が実行されルートが増えるほどパフォーマンスが悪化します。

Hono の RegExpRouter はルートパターンを "ひとつの巨大な正規表現" に変換します。
そのため一回の照合で結果を取得できます。

これはほとんどの場合、 radix-tree などのツリーベースのアルゴリズムより高速に動作します。
しかしながら、 RegExpRouter はすべてのルーティングパターンをサポートしているわけではありません。 そのため通常は、以下のルータを組み合わせて使用し、すべてのルーティングパターンをサポートします。
## TrieRouter
**TrieRouter** は Trie 木を使用するルーターです。
RegExpRouter と同様に線形ループを使用しません。

これは RegExpRouter ほど速くはありませんが、 Express よりは圧倒的に高速です。
TrieRouter は全てのルートパターンをサポートします。
## SmartRouter
**SmartRouter** は、複数のルーターを使用する際に役に立ちます。 登録済みのルーターから最もよいルータを推測して選択します。
Hono はデフォルトでは、 SmartRouter, RegExpRouter, TrieRouter を使用します。
```ts
// Inside the core of Hono.
readonly defaultRouter: Router = new SmartRouter({
routers: [new RegExpRouter(), new TrieRouter()],
})
```
アプリケーションが起動したときに、 SmartRouter はルーティングに基づき最速のルーターを選択し、使用します。
## LinearRouter
RegExpRouter は高速ですが、ルートの登録が少し遅くなることがあります。
そのため、リクエスト毎に初期化が行われる環境には適していません。
**LinearRouter** は "ワンショット" の状況に最適化されています。
ルートの登録はコンパイルせずに線形アプローチを使用して行うため RegExpRouter より大幅に高速です。
以下はルート登録も含めたベンチマークの結果です。
```console
• GET /user/lookup/username/hey
----------------------------------------------------- -----------------------------
LinearRouter 1.82 µs/iter (1.7 µs … 2.04 µs) 1.84 µs 2.04 µs 2.04 µs
MedleyRouter 4.44 µs/iter (4.34 µs … 4.54 µs) 4.48 µs 4.54 µs 4.54 µs
FindMyWay 60.36 µs/iter (45.5 µs … 1.9 ms) 59.88 µs 78.13 µs 82.92 µs
KoaTreeRouter 3.81 µs/iter (3.73 µs … 3.87 µs) 3.84 µs 3.87 µs 3.87 µs
TrekRouter 5.84 µs/iter (5.75 µs … 6.04 µs) 5.86 µs 6.04 µs 6.04 µs
summary for GET /user/lookup/username/hey
LinearRouter
2.1x faster than KoaTreeRouter
2.45x faster than MedleyRouter
3.21x faster than TrekRouter
33.24x faster than FindMyWay
```
## PatternRouter
**PatternRouter** は Hono で最も小さいルーターです。
Hono は既に十分コンパクトですが、リソースが限られている環境で更に小さくする必要があるなら PatternRouter を使用してください。
PatternRouter のみを使用したアプリケーションは 15KB 以下になります。
```console
$ npx wrangler deploy --minify ./src/index.ts
⛅️ wrangler 3.20.0
-------------------
Total Upload: 14.68 KiB / gzip: 5.38 KiB
```
# Hono Stacks
Hono は簡単なことを簡単にし、難しいことも簡単にします。
JSON を返すだけではなく、 REST API サーバーとクライアントを含むフルスタックアプリケーションを作るのにも最適です。
## RPC
Hono の RPC 機能を使用すると、コードをほとんど変更せずに API 仕様を共用できます。
`hc` により作成されたクライアントは仕様を読み取り型安全にエンドポイントへアクセスします。
以下のライブラリで実現できます。
- Hono - API サーバー
- [Zod](https://zod.dev) - バリデーター
- [Zod Validator Middleware](https://github.com/honojs/middleware/tree/main/packages/zod-validator)
- `hc` - HTTP クライアント
これらのコンポーネントのセットを **Hono Stack** と呼ぶことが出来ます。
API サーバーとそのクライアントを作ってみましょう。
## API を書く
まずは、 GET リクエストを受け取り JSON を返すエンドポイントを作成します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
app.get('/hello', (c) => {
return c.json({
message: `Hello!`,
})
})
```
## Zod でバリデーションする
Zod でクエリパラメータのバリデーションを行ってからデータを受け取ります。

```ts
import { zValidator } from '@hono/zod-validator'
import * as z from 'zod'
app.get(
'/hello',
zValidator(
'query',
z.object({
name: z.string(),
})
),
(c) => {
const { name } = c.req.valid('query')
return c.json({
message: `Hello! ${name}`,
})
}
)
```
## 型の共有
エンドポイント仕様を出力するには型をエクスポートします。
::: warning
RPC がルートを正しく推論するためには、含まれるすべてのメソッドがチェーンされている必要があり、エンドポイントやアプリの型は宣言された変数から推論される必要があります。 詳しくは [RPC のベストプラクティス](https://hono.dev/docs/guides/best-practices#if-you-want-to-use-rpc-features) をご覧ください。
:::
```ts{1,17}
const route = app.get(
'/hello',
zValidator(
'query',
z.object({
name: z.string(),
})
),
(c) => {
const { name } = c.req.valid('query')
return c.json({
message: `Hello! ${name}`,
})
}
)
export type AppType = typeof route
```
## クライアント
次はクライアントの実装です。
`AppType` 型をジェネリクスとして `hc` に渡してクライアントオブジェクトを作ります。
驚くほど補完が効き、エンドポイントのパスとリクエストの型がサジェストされます。

```ts
import type { AppType } from './server'
import { hc } from 'hono/client'
const client = hc('/api')
const res = await client.hello.$get({
query: {
name: 'Hono',
},
})
```
`Response` は fetch API と互換性がありますが、 `json()` で取得できるデータには型があります。

```ts
const data = await res.json()
console.log(`${data.message}`)
```
API 仕様を共用できるということは、サーバーサイドの変更を認識することが出来ます。

## React といっしょ
Cloudflare Workers で React を用いたアプリケーションを作成できます。
API サーバー:
```ts
// src/index.ts
import { Hono } from 'hono'
import * as z from 'zod'
import { zValidator } from '@hono/zod-validator'
const schema = z.object({
id: z.string(),
title: z.string(),
})
type Todo = z.infer
const todos: Todo[] = []
const api = new Hono()
.post('/todo', zValidator('form', schema), (c) => {
const todo = c.req.valid('form')
todos.push(todo)
return c.json({
message: 'created!',
})
})
.get('/todo', (c) => {
return c.json({
todos,
})
})
export type AppType = typeof api
const app = new Hono()
app.route('/api', api)
export default app
```
React と React Query を使ったクライアント:
```tsx
// src/App.tsx
import {
useQuery,
useMutation,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import type { AppType } from '../functions/api/[[route]]'
import { hc, InferResponseType, InferRequestType } from 'hono/client'
const queryClient = new QueryClient()
const client = hc('/api')
export default function App() {
return (
)
}
const Todos = () => {
const query = useQuery({
queryKey: ['todos'],
queryFn: async () => {
const res = await client.todo.$get()
return await res.json()
},
})
const $post = client.todo.$post
const mutation = useMutation<
InferResponseType,
Error,
InferRequestType['form']
>({
mutationFn: async (todo) => {
const res = await $post({
form: todo,
})
return await res.json()
},
onSuccess: async () => {
queryClient.invalidateQueries({ queryKey: ['todos'] })
},
onError: (error) => {
console.log(error)
},
})
return (
{query.data?.todos.map((todo) => (
- {todo.title}
))}
)
}
```
# Web 標準
Hono は Fetch のような **Web 標準**のみを使用します。
これらは元々 `fetch` 関数で使用され、 HTTP リクエストを処理する基本的なオブジェクトで構成されています。
`Requests` / `Responses` 、 他に `URL` `URLSearchParam` `Headers` などがあります。
Cloudflare Workers 、 Deno 、 Bun は Web 標準 API 上に構築されています。
例えば、 "Hello World" を返すサーバーは以下のように書くことが出来ます。 Cloudflare Workers と Bun で動作します。
```ts twoslash
export default {
async fetch() {
return new Response('Hello World')
},
}
```
Hono は Web 標準のみを使用します。 つまり Hono は Web 標準 API をサポートするあらゆるランタイムで動作します。
また Node.js もあり、次のようなランタイムで Hono が動作します。
- Cloudflare Workers (`workerd`)
- Deno
- Bun
- Fastly Compute
- AWS Lambda
- Node.js
- Vercel (edge-light)
- WebAssembly (w/ [WebAssembly System Interface (WASI)][wasi] via [`wasi:http`][wasi-http])
Netlify やその他のプラットフォームでも動作します。
同じコードがすべてのプラットフォームで実行されます。
Cloudflare Workers 、 Deno 、 Shopify などが [WinterCG](https://wintercg.org) を立ち上げ、 Web 標準を使用した "Web 相互運用性" を実現する可能性を議論しました。
Hono はその手順に従い **"Web 標準の標準"**を目指します。
[wasi]: https://github.com/WebAssembly/wasi
[wasi-http]: https://github.com/WebAssembly/wasi-http
# Context
`Context` オブジェクトはリクエストごとにインスタンス化され、レスポンスを返すまで使用されます。 ここにデータを置いたり、レスポンスのヘッダやステータスコードを設定したり、 HonoRequest や Response オブジェクトにアクセスしたりします。
## req
`req` は HonoRequest のインスタンスです。 詳しくは [HonoRequest](/docs/api/request) をご覧ください。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/hello', (c) => {
const userAgent = c.req.header('User-Agent')
// ...
// ---cut-start---
return c.text(`Hello, ${userAgent}`)
// ---cut-end---
})
```
## status()
HTTP ステータスコードを `c.status()` で設定できます。 デフォルトは 200 です。 ステータスコードを 200 に設定する場合、 `c.status()` を使用する必要はありません。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.post('/posts', (c) => {
// Set HTTP status code
c.status(201)
return c.text('Your post is created!')
})
```
## header()
レスポンスに HTTP ヘッダを設定できます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/', (c) => {
// Set headers
c.header('X-Message', 'My custom message')
return c.text('Hello!')
})
```
## body()
HTTP レスポンスを返します。
::: info
**Note**: テキストや HTML を返す場合は、 `c.text()` や `c.html()` を使ってください。
:::
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/welcome', (c) => {
c.header('Content-Type', 'text/plain')
// Return the response body
return c.body('Thank you for coming')
})
```
このように書くこともできます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/welcome', (c) => {
return c.body('Thank you for coming', 201, {
'X-Message': 'Hello!',
'Content-Type': 'text/plain',
})
})
```
このレスポンスは以下の `Response` オブジェクトと同じです。
```ts twoslash
new Response('Thank you for coming', {
status: 201,
headers: {
'X-Message': 'Hello!',
'Content-Type': 'text/plain',
},
})
```
## text()
`Content-Type: text/plain` でテキストをレンダリングします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/say', (c) => {
return c.text('Hello!')
})
```
## json()
`Content-Type: application/json` で JSON をレンダリングします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/api', (c) => {
return c.json({ message: 'Hello!' })
})
```
## html()
`Content-Type: text/html` で HTML をレンダリングします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/', (c) => {
return c.html('Hello! Hono!
')
})
```
## notFound()
`Not Found` レスポンスを返します。 [`app.notFound()`](/docs/api/hono#not-found) でカスタマイズできます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/notfound', (c) => {
return c.notFound()
})
```
## redirect()
リダイレクトします。 デフォルトのステータスコードは `302` です。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/redirect', (c) => {
return c.redirect('/')
})
app.get('/redirect-permanently', (c) => {
return c.redirect('/', 301)
})
```
## res
送信される [Response] オブジェクトにアクセスできます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
// Response object
app.use('/', async (c, next) => {
await next()
c.res.headers.append('X-Debug', 'Debug message')
})
```
[Response]: https://developer.mozilla.org/en-US/docs/Web/API/Response
## set() / get()
リクエストの間の寿命を持つ任意のキーバリューのペアを設定、取得できます。 これにより、ミドルウェア間やミドルウェア、ルートハンドラ間でデータを渡すことができます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono<{ Variables: { message: string } }>()
// ---cut---
app.use(async (c, next) => {
c.set('message', 'Hono is cool!!')
await next()
})
app.get('/', (c) => {
const message = c.get('message')
return c.text(`The message is "${message}"`)
})
```
`Variables` ジェネリクスを `Hono` に渡すと型安全になります。
```ts twoslash
import { Hono } from 'hono'
// ---cut---
type Variables = {
message: string
}
const app = new Hono<{ Variables: Variables }>()
```
`c.set` / `c.get` は同じリクエスト内でのみ保持されます。 違うリクエストの間では共有されません。
## var
`c.var` を使用しても変数の値にアクセスできます。
```ts twoslash
import type { Context } from 'hono'
declare const c: Context
// ---cut---
const result = c.var.client.oneMethod()
```
カスタムメソッドを提供するミドルウェアを作成したい場合は、
このように書きます:
```ts twoslash
import { Hono } from 'hono'
import { createMiddleware } from 'hono/factory'
// ---cut---
type Env = {
Variables: {
echo: (str: string) => string
}
}
const app = new Hono()
const echoMiddleware = createMiddleware(async (c, next) => {
c.set('echo', (str) => str)
await next()
})
app.get('/echo', echoMiddleware, (c) => {
return c.text(c.var.echo('Hello!'))
})
```
複数のハンドラでミドルウェアを使いたい場合、 `app.use()` を使います。
次に、 `Env` ジェネリクスを `Hono` コンストラクタに渡して型安全にするべきです。
```ts twoslash
import { Hono } from 'hono'
import type { MiddlewareHandler } from 'hono/types'
declare const echoMiddleware: MiddlewareHandler
type Env = {
Variables: {
echo: (str: string) => string
}
}
// ---cut---
const app = new Hono()
app.use(echoMiddleware)
app.get('/echo', (c) => {
return c.text(c.var.echo('Hello!'))
})
```
## render() / setRenderer()
カスタムミドルウェア内で `c.setRenderer()` を使用してレイアウトを設定できます。
```tsx twoslash
/** @jsx jsx */
/** @jsxImportSource hono/jsx */
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.use(async (c, next) => {
c.setRenderer((content) => {
return c.html(
{content}
)
})
await next()
})
```
次に、 `c.render()` を使用してそのレイアウトでレスポンスを作成します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/', (c) => {
return c.render('Hello!')
})
```
このような出力になります:
```html
Hello!
```
また、この機能は柔軟に引数を設定することもできます。
型安全のために、型を次のように設定できます:
```ts
declare module 'hono' {
interface ContextRenderer {
(
content: string | Promise,
head: { title: string }
): Response | Promise
}
}
```
使用例を以下に示します:
```ts
app.use('/pages/*', async (c, next) => {
c.setRenderer((content, head) => {
return c.html(
{head.title}
{content}
)
})
await next()
})
app.get('/pages/my-favorite', (c) => {
return c.render(Ramen and Sushi
, {
title: 'My favorite',
})
})
app.get('/pages/my-hobbies', (c) => {
return c.render(Watching baseball
, {
title: 'My hobbies',
})
})
```
## executionCtx
Cloudflare Workers の [ExecutionContext](https://developers.cloudflare.com/workers/runtime-apis/context/) にアクセスできます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono<{
Bindings: {
KV: any
}
}>()
declare const key: string
declare const data: string
// ---cut---
// ExecutionContext object
app.get('/foo', async (c) => {
c.executionCtx.waitUntil(c.env.KV.put(key, data))
// ...
})
```
`ExecutionContext` には、 [`exports`](https://developers.cloudflare.com/workers/runtime-apis/context/#exports) フィールドもあります。 Wrangler が生成する型でオートコンプリートさせるには、モジュール拡張を使用することができます:
```ts
import 'hono'
declare module 'hono' {
interface ExecutionContext {
readonly exports: Cloudflare.Exports
}
}
```
## event
Cloudflare Workers の `FetchEvent` にアクセスできます。 これは "Service Worker" 構文で使用されていましたが、現在は非推奨です。
```ts twoslash
import { Hono } from 'hono'
declare const key: string
declare const data: string
type KVNamespace = any
// ---cut---
// Type definition to make type inference
type Bindings = {
MY_KV: KVNamespace
}
const app = new Hono<{ Bindings: Bindings }>()
// FetchEvent object (only set when using Service Worker syntax)
app.get('/foo', async (c) => {
c.event.waitUntil(c.env.MY_KV.put(key, data))
// ...
})
```
## env
Cloudflare Workers の環境変数、シークレット、 KV ネームスペース、 D1 データベース、 R2 バケット等... をバインディングと呼びます。
種類に関係なく、バインディングは常にグローバル変数として利用でき、 `c.env.BINDING_KEY` からアクセスできます。
```ts twoslash
import { Hono } from 'hono'
type KVNamespace = any
// ---cut---
// Type definition to make type inference
type Bindings = {
MY_KV: KVNamespace
}
const app = new Hono<{ Bindings: Bindings }>()
// Environment object for Cloudflare Workers
app.get('/', async (c) => {
c.env.MY_KV.get('my-key')
// ...
})
```
## error
ハンドラでエラーが発生した場合、エラーオブジェクトは `c.error` に格納されます。
ミドルウェアからアクセスできます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.use(async (c, next) => {
await next()
if (c.error) {
// do something...
}
})
```
## ContextVariableMap
::: warning
`ContextVariableMap` は、変数をセットしたミドルウェアが実際に実行しているかどうかにかかわらず、すべてのコンテキストに**グローバルに**型を追加します。 `c.get('result')` が、ミドルウェアが何も登録されていないハンドラ内であっても型安全であり、ランタイム側で部分的に `undefined` バグを隠しているということが言えます。
次の例を見てください:
```ts
declare module 'hono' {
interface ContextVariableMap {
result: string
}
}
const mw = createMiddleware(async (c, next) => {
c.set('result', 'some values')
await next()
})
const app = new Hono()
// ハンドラはミドルウェアを使用します
app.get('/foo', mw, (c) => {
const val = c.get('result') // ✅ val は文字列で、文字列に型づけされる。 期待どおり。
})
// ハンドラはミドルウェアを使用しない
app.get('/bar', (c) => {
const val = c.get('result') // ❌ val は undefined だが、文字列に型づけされる。 ランタイムエラーになる可能性がある。
})
```
:::
アプリケーション全体に渡ってグローバルにコンテキストの変数として型を定義するために `ContextVariableMap` インタフェースを拡張することができます。 変数がアプリケーション全体に適用されるミドルウェアでセットされて、コンテキスト内に確実に存在することが保証される場合に適しています。
例:
```ts
declare module 'hono' {
interface ContextVariableMap {
result: string
}
}
```
これをミドルウェアで利用できます:
```ts twoslash
import { createMiddleware } from 'hono/factory'
// ---cut---
const mw = createMiddleware(async (c, next) => {
c.set('result', 'some values') // result is a string
await next()
})
```
ハンドラで、変数は適切な型を推論されます:
```ts twoslash
import { Hono } from 'hono'
const app = new Hono<{ Variables: { result: string } }>()
// ---cut---
app.get('/', (c) => {
const val = c.get('result') // val is a string
// ...
return c.json({ result: val })
})
```
# HTTPException
致命的なエラーが発生した際に、Hono (や多くのエコシステムミドルウェア) は、`HTTPException` をスローするかもしれません。 これは、[エラーレスポンスを返す](#handling-httpexceptions) を簡素化したカスタム Hono `Error` です。
## HTTPException をスローする
ステータスコードとエラーメッセージあるいはカスタムレスポンスのどちらかを指定することで、独自の HTTPException をスローすることができます。
### カスタムメッセージ
基本的な `text` レスポンスを返すために、エラー `message` をセットするだけです。
```ts twoslash
import { HTTPException } from 'hono/http-exception'
throw new HTTPException(401, { message: 'Unauthorized' })
```
### カスタムレスポンス
他のレスポンス型のために、あるいはレスポンスヘッダをセットするために、 `res` オプションを使用します。 _コンストラクタに渡されたステータスは、レスポンスを生成するために使用されたものであることに注意してください_
```ts twoslash
import { HTTPException } from 'hono/http-exception'
const errorResponse = new Response('Unauthorized', {
status: 401, // これは無視されます
headers: {
Authenticate: 'error="invalid_token"',
},
})
throw new HTTPException(401, { res: errorResponse })
```
### Cause
いずれの場合も、 HTTPException に任意のデータを追加するために [`cause`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) オプションを使用することができます。
```ts twoslash
import { Hono, Context } from 'hono'
import { HTTPException } from 'hono/http-exception'
const app = new Hono()
declare const message: string
declare const authorize: (c: Context) => Promise
// ---cut---
app.post('/login', async (c) => {
try {
await authorize(c)
} catch (cause) {
throw new HTTPException(401, { message, cause })
}
return c.redirect('/')
})
```
## HTTPException を処理する
[`app.onError`](/docs/api/hono#error-handling) を使用して、捕捉されなかった HTTPException を処理することができます。 HTTPException は `getResponse` メソッドを持っており、エラー `status` をもとに生成された新しい `Response` を返します。 エラーがスローされた際に、エラー `message` あるいは[カスタムレスポンス](#custom-response)のどちらかがセットされます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
import { HTTPException } from 'hono/http-exception'
// ...
app.onError((err, c) => {
if (err instanceof HTTPException) {
// HTTPException で生成されたエラーレスポンスを返す
return err.getResponse()
}
// 他の予期しないエラーが起きたら、ログを出力してから一般的な 500 レスポンスを返します
console.error(err)
return c.text('Internal Server Error', 500)
})
```
::: warning
**`HTTPException.getResponse` は、 `Context` を認識しません** `Context` で既にセットされたヘッダを含めるには、新しい `Response` にヘッダを適用しなければなりません。
:::
# App - Hono
`Hono` は中心的なオブジェクトです。
最初にインポートされ、最後まで使用されます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
//...
export default app // for Cloudflare Workers or Bun
```
## メソッド
`Hono` のインスタンスには以下のメソッドがあります。
- app.**HTTP_METHOD**(\[path,\]handler|middleware...)
- app.**all**(\[path,\]handler|middleware...)
- app.**on**(method|method[], path|path[], handler|middleware...)
- app.**use**(\[path,\]middleware)
- app.**route**(path, \[app\])
- app.**basePath**(path)
- app.**notFound**(handler)
- app.**onError**(err, handler)
- app.**mount**(path, anotherApp, \[options\])
- app.**fire**()
- app.**fetch**(request, env, event)
- app.**request**(path, options)
これらの前半の部分はルーティングで使用されます。 [ルーティング](/docs/api/routing)を読んでください。
## Not Found
`app.notFound` を使用して Not Found レスポンスをカスタマイズできます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.notFound((c) => {
return c.text('Custom 404 Message', 404)
})
```
:::warning
`notFound` メソッドは、トップレベルのアプリケーションからのみコールされます。 詳細については [issue](https://github.com/honojs/hono/issues/3465#issuecomment-2381210165) を参照してください。
:::
## エラーハンドリング
`app.onError` は処理されなかったエラーをハンドルし、カスタマイズしたレスポンスを返すことが出来ます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.onError((err, c) => {
console.error(`${err}`)
return c.text('Custom Error Message', 500)
})
```
::: info
親アプリケーションとそのルートの両方に `onError` ハンドラがある場合、ルートレベルのハンドラが優先されます。
:::
## fire()
::: warning
**`app.fire()` は非推奨です**。 代わりに `@hono/service-worker` から `fire()` を使ってください。 詳しくは[サービスワーカーのドキュメント](/docs/getting-started/service-worker) を読んでください。
:::
`app.fire()` は自動で `fetch` イベントリスナーを追加します。
これは [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) を使用している [non-ES module Cloudflare Workers](https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/) のような環境で上手く機能します。
`app.fire()` はあなたの代わりに以下の作業を行います:
```ts
addEventListener('fetch', (event: FetchEventLike): void => {
event.respondWith(this.dispatch(...))
})
```
## fetch()
`app.fetch` はアプリケーションのエントリポイントです。
Cloudflare Workers ではこのように使用できます:
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
type Env = any
type ExecutionContext = any
// ---cut---
export default {
fetch(request: Request, env: Env, ctx: ExecutionContext) {
return app.fetch(request, env, ctx)
},
}
```
もしくは:
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
export default app
```
Bun:
```ts
export default app // [!code --]
export default { // [!code ++]
port: 3000, // [!code ++]
fetch: app.fetch, // [!code ++]
} // [!code ++]
```
## request()
`request` はテストに便利なメソッドです。
URL かパスを渡して GET リクエストを送信します。
`app` は `Response` オブジェクトを返します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
declare const test: (name: string, fn: () => void) => void
declare const expect: (value: any) => any
// ---cut---
test('GET /hello is ok', async () => {
const res = await app.request('/hello')
expect(res.status).toBe(200)
})
```
また、 `Request` オブジェクトを渡すことも出来ます:
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
declare const test: (name: string, fn: () => void) => void
declare const expect: (value: any) => any
// ---cut---
test('POST /message is ok', async () => {
const req = new Request('Hello!', {
method: 'POST',
})
const res = await app.request(req)
expect(res.status).toBe(201)
})
```
## mount()
::: warning
**`app.mount()` は非推奨です**。 [Mount ミドルウェア](/docs/middleware/builtin/mount)を代わりに使ってください。
:::
`mount()` は他のフレームワークで書かれたアプリケーションを Hono のアプリケーションにマウントできます。
```ts
import { Router as IttyRouter } from 'itty-router'
import { Hono } from 'hono'
// Create itty-router application
const ittyRouter = IttyRouter()
// Handle `GET /itty-router/hello`
ittyRouter.get('/hello', () => new Response('Hello from itty-router'))
// Hono application
const app = new Hono()
// Mount!
app.mount('/itty-router', ittyRouter.handle)
```
デフォルトでは、 `mount()` はマウントパスを URL から取り除いた新しい `Request` を渡します。 マウントされたアプリケーションにどの `Request` を渡すかを制御するには、 `replaceRequest` 関数を指定してください:
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
const handler = (request: Request) => new Response(request.url)
// ---cut---
app.mount('/app', handler, {
replaceRequest: (originalRequest) => originalRequest,
})
```
元の `Request` をそのまま渡したい場合は、上記の関数の省略形として `replaceRequest` に `false` を設定してください:
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
const handler = (request: Request) => new Response(request.url)
// ---cut---
app.mount('/app', handler, {
replaceRequest: false,
})
```
## strict mode
strict mode はデフォルトで `true` で、以下のルートが区別されます。
- `/hello`
- `/hello/`
`app.get('/hello')` は `GET /hello/` にマッチしません。
strict mode を `false` に設定した場合、2つのルートは等しくなります。
```ts twoslash
import { Hono } from 'hono'
// ---cut---
const app = new Hono({ strict: false })
```
## ルーターオプション
`router` オプションはどのルーターを使うか指定できます。 デフォルトでは `SmartRouter` が使われます。 `RegExpRouter` を使いたい場合、このように `Hono` のインスタンスを生成してください。
```ts twoslash
import { Hono } from 'hono'
// ---cut---
import { RegExpRouter } from 'hono/router/reg-exp-router'
const app = new Hono({ router: new RegExpRouter() })
```
## ジェネリクス
ジェネリクスを使用して、 `c.set` / `c.get` で使用される Cloudflare Workers バインディングと変数を追加します。
```ts twoslash
import { Hono } from 'hono'
type User = any
declare const user: User
// ---cut---
type Bindings = {
TOKEN: string
}
type Variables = {
user: User
}
const app = new Hono<{
Bindings: Bindings
Variables: Variables
}>()
app.use('/auth/*', async (c, next) => {
const token = c.env.TOKEN // token is `string`
// ...
c.set('user', user) // user should be `User`
await next()
})
```
# API
Hono の API はシンプルです。
Web Standard API を拡張したオブジェクトで構成されています。
そのため、簡単に理解できます。
この章では以下のような Hono の API を説明します。
- Hono オブジェクト
- ルーティング
- Context オブジェクト
- ミドルウェア
# プリセット
Hono にはいくつかのルーターがあり、それぞれが特定の目的のために作られています。
Hono のコンストラクタで使用するルーターを指定できます。
**プリセット** は一般的な仕様ケースに合わせて提供されるため、毎回ルーターを指定する必要はありません。
すべてのプリセットからインポートされた `Hono` クラスは同じものであり、ルーターだけが違います。
そのため、交換が可能です。
## `hono`
使い方:
```ts twoslash
import { Hono } from 'hono'
```
ルーター:
```ts
this.router = new SmartRouter({
routers: [new RegExpRouter(), new TrieRouter()],
})
```
## `hono/quick`
使い方 :
```ts twoslash
import { Hono } from 'hono/quick'
```
ルーター:
```ts
this.router = new SmartRouter({
routers: [new LinearRouter(), new TrieRouter()],
})
```
## `hono/tiny`
使い方:
```ts twoslash
import { Hono } from 'hono/tiny'
```
ルーター:
```ts
this.router = new PatternRouter()
```
## どのプリセットを使うべきですか?
| プリセット | 適切なプラットフォーム |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `hono` | ほとんどのユースケースで特におすすめされます。 ルート登録は `hono/quick` より遅いかもしれませんが、起動してしまえば高速です。 **Deno** 、 **Bun** 、 **Node.js** で構築された常時起動サーバーに最適です。 ルート登録はプラットフォーム上のアプリのビルド段階で行われるため、 **Fastly Compute** にも適しています。 分離環境は起動後、一定時間持続するため、 V8 Isolates が使用される **Cloudflare Workers** 、 **Deno Deploy** にも適しています。 |
| `hono/quick` | リクエストごとにアプリケーションが初期化される環境向けに設計されています。 |
| `hono/tiny` | 最も小さいルーターパッケージで、リソースが限られている環境に適しています。 |
# HonoRequest
`HonoRequest` は [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) をラップするオブジェクトで、 `c.req` からアクセスできます。
## param()
パスパラメータの値を取得します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
// Captured params
app.get('/entry/:id', async (c) => {
const id = c.req.param('id')
// ^?
// ...
})
// Get all params at once
app.get('/entry/:id/comment/:commentId', async (c) => {
const { id, commentId } = c.req.param()
// ^?
})
```
## query()
クエリパラメータを取得します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
// Query params
app.get('/search', async (c) => {
const query = c.req.query('q')
// ^?
})
// Get all params at once
app.get('/search', async (c) => {
const { q, limit, offset } = c.req.query()
// ^?
})
```
## queries()
複数のクエリパラメータを取得します。 例: `/search?tags=A&tags=B`
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/search', async (c) => {
// tags will be string[]
const tags = c.req.queries('tags')
// ^?
// ...
})
```
## header()
リクエストのヘッダを取得します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/', (c) => {
const userAgent = c.req.header('User-Agent')
// ^?
return c.text(`Your user agent is ${userAgent}`)
})
```
::: warning
引数無しで `c.req.header()` が呼ばれた場合、すべてのレコードのキーは **小文字** になって返されます。
大文字を使ったヘッダ名で値を取得したい場合は
`c.req.header(“X-Foo”)` のように使います。
```ts
// ❌ Will not work
const headerRecord = c.req.header()
const foo = headerRecord['X-Foo']
// ✅ Will work
const foo = c.req.header('X-Foo')
```
:::
## parseBody()
`multipart/form-data` または `application/x-www-form-urlencoded` のリクエストボディをパースします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.post('/entry', async (c) => {
const body = await c.req.parseBody()
// ...
})
```
`parseBody()` は次の動作をサポートします。
**単一ファイル**
```ts twoslash
import { Context } from 'hono'
declare const c: Context
// ---cut---
const body = await c.req.parseBody()
const data = body['foo']
// ^?
```
`body['foo']` は `(string | File)` です。
複数のファイルがアップロードされた場合、最後のファイルが取得されます。
### 複数ファイル
```ts twoslash
import { Context } from 'hono'
declare const c: Context
// ---cut---
const body = await c.req.parseBody()
body['foo[]']
```
`body['foo[]']` は常に `(string | File)[]` です。
`[]` ポストフィックスが必要です。
### 同じ名前の複数ファイルやフィールド
複数の `` や同じ名前の `` を持つ複数のチェックボックスを有効とする input フィールドがある場合
```ts twoslash
import { Context } from 'hono'
declare const c: Context
// ---cut---
const body = await c.req.parseBody({ all: true })
body['foo']
```
`all` オプションはデフォルトで無効です。
- `body['foo']` が複数ファイルだった場合、 `(string | File)[]` にパースされます。
- `body['foo']` が単一ファイルだった場合、 `(string | File)` にパースされます。
### ドット表記
`dot` オプションを `true` にした場合、戻り値はドット表記基づいて構造化されます。
このようなデータを受け取ることを考えてください:
```ts twoslash
const data = new FormData()
data.append('obj.key1', 'value1')
data.append('obj.key2', 'value2')
```
`dot` オプションを `true` にすることで構造化された値を取得することができます:
```ts twoslash
import { Context } from 'hono'
declare const c: Context
// ---cut---
const body = await c.req.parseBody({ dot: true })
// body is `{ obj: { key1: 'value1', key2: 'value2' } }`
```
## json()
`application/json` のリクエストボディをパースします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.post('/entry', async (c) => {
const body = await c.req.json()
// ...
})
```
## text()
`text/plain` のリクエストボディをパースします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.post('/entry', async (c) => {
const body = await c.req.text()
// ...
})
```
## arrayBuffer()
リクエストボディを `ArrayBuffer` としてパースします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.post('/entry', async (c) => {
const body = await c.req.arrayBuffer()
// ...
})
```
## blob()
リクエストボディを `Blob` としてパースします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.post('/entry', async (c) => {
const body = await c.req.blob()
// ...
})
```
## formData()
リクエストボディを `FormData` としてパースします。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.post('/entry', async (c) => {
const body = await c.req.formData()
// ...
})
```
## valid()
バリデーションされたデータを取得します。
```ts
app.post('/posts', async (c) => {
const { title, body } = c.req.valid('form')
// ...
})
```
これらのデータに対して利用可能です。
- `form`
- `json`
- `query`
- `header`
- `cookie`
- `param`
[バリデーションセクション](/docs/guides/validation)で利用例を見てください。
## routePath
::: warning
**Deprecated in v4.8.0**: このプロパティは非推奨です。 代わりに [Route Helper](/docs/helpers/route) から `routePath()` を使用してください
:::
ハンドラ内で定義されたパスをこのように取得できます:
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/posts/:id', (c) => {
return c.json({ path: c.req.routePath })
})
```
`/posts/123` にアクセスしたとき、 `/posts/:id` が返されます:
```json
{ "path": "/posts/:id" }
```
## matchedRoutes
::: warning
**Deprecated in v4.8.0**: このプロパティは非推奨です。 代わりに [Route Helper](/docs/helpers/route) から `matchedRoutes()` を使用してください
:::
ハンドラで一致したルートを返します、デバッグに適しています。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.use(async function logger(c, next) {
await next()
c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
const name =
handler.name ||
(handler.length < 2 ? '[handler]' : '[middleware]')
console.log(
method,
' ',
path,
' '.repeat(Math.max(10 - path.length, 0)),
name,
i === c.req.routeIndex ? '<- respond from here' : ''
)
})
})
```
## path
リクエストのパス。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/about/me', async (c) => {
const pathname = c.req.path // `/about/me`
// ...
})
```
## url
リクエストの URL 文字列。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/about/me', async (c) => {
const url = c.req.url // `http://localhost:8787/about/me`
// ...
})
```
## method
リクエストの HTTP メソッド名。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/about/me', async (c) => {
const method = c.req.method // `GET`
// ...
})
```
## raw
生の [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) オブジェクト。
```ts
// For Cloudflare Workers
app.post('/', async (c) => {
const metadata = c.req.raw.cf?.hostMetadata
// ...
})
```
## cloneRawRequest()
HonoRequest から生のリクエストオブジェクトをクローンします。 リクエストボディがバリデータや HonoRequest メソッドによって処理された後でも動作します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
import { cloneRawRequest } from 'hono/request'
import { validator } from 'hono/validator'
app.post(
'/forward',
validator('json', (data) => data),
async (c) => {
// バリデーションの後にクローンする
const clonedReq = await cloneRawRequest(c.req)
// エラーをスローしない
await clonedReq.json()
// ...
}
)
```
# ルーティング
Hono のルーティングは柔軟で直感的です。
では見てみましょう。
## 基本
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
// HTTP Methods
app.get('/', (c) => c.text('GET /'))
app.post('/', (c) => c.text('POST /'))
app.put('/', (c) => c.text('PUT /'))
app.delete('/', (c) => c.text('DELETE /'))
app.query('/', (c) => c.text('QUERY /'))
// Wildcard
app.get('/wild/*/card', (c) => {
return c.text('GET /wild/*/card')
})
// Any HTTP methods
app.all('/hello', (c) => c.text('Any Method /hello'))
// Custom HTTP method
app.on('PURGE', '/cache', (c) => c.text('PURGE Method /cache'))
// Multiple Method
app.on(['PUT', 'DELETE'], '/post', (c) =>
c.text('PUT or DELETE /post')
)
// Multiple Paths
app.on('GET', ['/hello', '/ja/hello', '/en/hello'], (c) =>
c.text('Hello')
)
```
## パスパラメータ
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/user/:name', async (c) => {
const name = c.req.param('name')
// ^?
// ...
})
```
または一度に多くのパラメータを使用できます:
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/posts/:id/comment/:comment_id', async (c) => {
const { id, comment_id } = c.req.param()
// ^?
// ...
})
```
## オプションのパラメータ
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
// Will match `/api/animal` and `/api/animal/:type`
app.get('/api/animal/:type?', (c) => c.text('Animal!'))
```
## 正規表現
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', async (c) => {
const { date, title } = c.req.param()
// ^?
// ...
})
```
## スラッシュを含む
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/posts/:filename{.+\\.png}', async (c) => {
//...
})
```
## メソッドチェーンでルーティングする
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app
.get('/endpoint', (c) => {
return c.text('GET /endpoint')
})
.post((c) => {
return c.text('POST /endpoint')
})
.delete((c) => {
return c.text('DELETE /endpoint')
})
```
## グループ化
Hono インスタンスを使用してルートをグループ化し、メインのアプリケーションで route メソッドを使用して追加します。
```ts twoslash
import { Hono } from 'hono'
// ---cut---
const book = new Hono()
book.get('/', (c) => c.text('List Books')) // GET /book
book.get('/:id', (c) => {
// GET /book/:id
const id = c.req.param('id')
return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book
const app = new Hono()
app.route('/book', book)
```
## ベースパスを変更せずにグループ化する
ベースパスをそのままに複数のインスタンスをグループ化することもできます。
```ts twoslash
import { Hono } from 'hono'
// ---cut---
const book = new Hono()
book.get('/book', (c) => c.text('List Books')) // GET /book
book.post('/book', (c) => c.text('Create Book')) // POST /book
const user = new Hono().basePath('/user')
user.get('/', (c) => c.text('List Users')) // GET /user
user.post('/', (c) => c.text('Create User')) // POST /user
const app = new Hono()
app.route('/', book) // Handle /book
app.route('/', user) // Handle /user
```
## ベースパス
ベースパスを指定できます。
```ts twoslash
import { Hono } from 'hono'
// ---cut---
const api = new Hono().basePath('/api')
api.get('/book', (c) => c.text('List Books')) // GET /api/book
```
## ホスト名でルーティングする
ホスト名をルートに含めても動作します。
```ts twoslash
import { Hono } from 'hono'
// ---cut---
const app = new Hono({
getPath: (req) => req.url.replace(/^https?:\/([^?]+).*$/, '$1'),
})
app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
app.get('/www2.example.com/hello', (c) => c.text('hello www2'))
```
## `host` ヘッダを使用したルーティング
Hono コンストラクタに `getPath()` 関数を実装すると `host` ヘッダの値を処理できます。
```ts twoslash
import { Hono } from 'hono'
// ---cut---
const app = new Hono({
getPath: (req) =>
'/' +
req.headers.get('host') +
req.url.replace(/^https?:\/\/[^/]+(\/[^?]*).*/, '$1'),
})
app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
// 以下のリクエストはルートにマッチします:
// new Request('http://www1.example.com/hello', {
// headers: { host: 'www1.example.com' },
// })
```
これを応用すれば、 `User-Agent` ヘッダでルーティングを変更するようなことも出来ます。
## ルーティングの優先順序
ハンドラ、ミドルウェアは登録順に実行されます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/book/a', (c) => c.text('a')) // a
app.get('/book/:slug', (c) => c.text('common')) // common
```
```
GET /book/a ---> `a`
GET /book/b ---> `common`
```
ハンドラが実行されると処理が停止します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('*', (c) => c.text('common')) // common
app.get('/foo', (c) => c.text('foo')) // foo
```
```
GET /foo ---> `common` // foo will not be dispatched
```
実行したいミドルウェアがある場合は、ハンドラよりも前に記述します。
```ts twoslash
import { Hono } from 'hono'
import { logger } from 'hono/logger'
const app = new Hono()
// ---cut---
app.use(logger())
app.get('/foo', (c) => c.text('foo'))
```
”_フォールバック_" ハンドラが必要な場合は、他のハンドラの下にコードを書きます。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
// ---cut---
app.get('/bar', (c) => c.text('bar')) // bar
app.get('*', (c) => c.text('fallback')) // fallback
```
```
GET /bar ---> `bar`
GET /foo ---> `fallback`
```
## グループの順序
ルーティングのグループ化の間違いは気が付きにくいので気をつけてください。
`route()` 関数は2番目の引数 ( `three` や `two` のような) から保存されたルーティングを取得し、自分自身 ( `two` や `app` ) のルートに追加します。
```ts
three.get('/hi', (c) => c.text('hi'))
two.route('/three', three)
app.route('/two', two)
export default app
```
これは 200 レスポンスを返しますが、
```
GET /two/three/hi ---> `hi`
```
順序が間違っている場合は 404 を返します。
```ts twoslash
import { Hono } from 'hono'
const app = new Hono()
const two = new Hono()
const three = new Hono()
// ---cut---
three.get('/hi', (c) => c.text('hi'))
app.route('/two', two) // `two` does not have routes
two.route('/three', three)
export default app
```
```
GET /two/three/hi ---> 404 Not Found
```
# Hono CLI
Hono CLI (`hono`) は Hono のコマンドラインツールで、コーディングエージェント向けに作られています。 Hono アプリを直接読み込むため、エージェントはサーバーを起動せずにアプリを検査・テストできます。 コマンドはデフォルトで JSON を出力します。 人間が読むときは `routes`, `request`, `benchmark`, `ssg` に `--plain` を指定します。
## インストール
プロジェクトにインストールします。 コーディングエージェントは `package.json` からそれを見つけます。
```sh
npm install -D @hono/cli
```
またはグローバルに:
```sh
npm install -g @hono/cli
```
## 出力
コマンドは JSON を標準出力に出力します。 `snapshot` は代わりにバッチ JSONL 行を出力します。
- 成功: `{ "ok": true, "data": ... }` 終了コード 0
- 失敗: `{ "ok": false, "error": { "code", "message", "suggestions", "docs" } }` 終了コード 1
失敗時、`error.suggestions` は次に試すべきことを示し、`error.docs` はこのサイトのページを指します。 ログは標準エラー出力に出力されます。
`hono --help` はコーディングエージェント向けの短い注記から始まり、`hono --help` には各コマンドの例と注記があります。 エージェントが CLI を使うために必要なものは他にありません。
## コマンド
| コマンド | 内容 |
| ---------------------------- | ------------------------------------------------------ |
| `hono routes [file]` | Hono アプリのルートを表示します |
| `hono request [path] [file]` | `app.request()` を使ってアプリにリクエストを送信します |
| `hono batch [file]` | JSONL から複数のリクエストを `app.request()` で実行します |
| `hono snapshot [file]` | 現在の挙動をバッチ JSONL 行として出力します |
| `hono diff` | 未コミットの変更がアプリに与える影響を表示します |
| `hono benchmark [file]` | Hono アプリのパフォーマンスを計測します |
| `hono ssg [file]` | Hono アプリから静的ファイルを生成します |
| `hono init` | 現在のディレクトリに Hono アプリを追加します |
`file` はアプリファイルへのパスです。 省略された場合、アプリは `src/index.ts`, `src/index.tsx`, `src/index.js`, または `src/index.jsx` から見つけられます。 TypeScript と JSX がサポートされています。
### routes
Hono アプリのすべてのルートを表示します。 [`showRoutes()`](/docs/helpers/dev#showroutes) のように。 ルートは実際のアプリインスタンスから解決されるため、マウントされたサブアプリや `basePath` が展開されます。
```sh
hono routes
hono routes --verbose src/app.ts
```
- `--verbose` - ミドルウェアを含める
- `--plain` - JSON の代わりに人間が読みやすい出力
- `-e, --external ` - パッケージを外部としてマークする (複数回使用可能)
```json
{
"ok": true,
"data": {
"router": "SmartRouter + RegExpRouter",
"routes": [
{
"method": "GET",
"path": "/",
"name": "[handler]",
"isMiddleware": false
},
{
"method": "POST",
"path": "/posts",
"name": "[handler]",
"isMiddleware": false
}
]
}
}
```
### request
`app.request()` を通じてアプリにリクエストを送信します。 サーバーは不要です。 `path` のデフォルトは `/` です。
```sh
hono request /
hono request /users/123
hono request /api/users -X POST -d '{"name":"Alice"}'
hono request /todos -X POST -d 'title=milk'
hono request /api/protected -H 'Authorization: Bearer token'
cat payload.json | hono request /api/users -X POST -d @-
hono request /api/users/123 --trace
hono request / --runtime bun
```
- `-X, --method ` - HTTP メソッド (デフォルト: `GET`)
- `-d, --data ` - リクエストボディ (`@file` はファイルを読み、`@-` は標準入力を読みます)。 JSON オブジェクトまたは配列は JSON として、それ以外はフォームとして送信されます。 ただし `-H` で `Content-Type` を設定した場合は除きます。
- `-H, --header ` - カスタムヘッダー (複数回使用可能)
- `--trace` - マッチしたルートを出力に含める
- `--runtime ` - アプリを `node` (デフォルト), `bun`, `deno`, `workerd`, または `vite` で実行する
- `--compact` - ヘッダーなしの一行 JSON
- `--no-bindings` - ローカルの Cloudflare バインディングの読み込みをスキップする
- `-w, --watch` - 変更を監視し、リクエストを再送する
- `-o, --output ` - レスポンスボディをファイルに書き込む
- `--plain` - curl のように生のボディを出力する (`-i` はステータスとヘッダーを追加、`-I` はそれらのみ表示)
JSON レスポンスボディはオブジェクトとして埋め込まれます:
```json
{
"ok": true,
"data": {
"status": 200,
"headers": { "content-type": "application/json" },
"body": { "message": "Hello World" }
}
}
```
`--trace` を使うと、出力に `matchedRoutes` が含まれます: どのミドルウェアとハンドラーがマッチし、どれが応答したかです。 404 の結果はそれを実行することを示唆します。
`-` をファイルとして標準入力からアプリコードを渡すこともできます。 `app` は事前定義され、あなたのためにエクスポートされています:
```sh
echo 'app.get("/hello", (c) => c.json({ ok: true }))' | hono request /hello -
```
#### Cloudflare バインディング
wrangler 設定があるプロジェクトでは、`c.env` が実際のローカルバインディング (KV, D1, R2, vars) を自動的に持ち、アプリは Node.js 上で実行されます。 これは `request`, `batch`, `snapshot`, `ssg` で動作します。 `--no-bindings` でスキップできます。 プロジェクトに [wrangler](https://developers.cloudflare.com/workers/wrangler/) がインストールされている必要があります。
`--runtime workerd` は代わりに wrangler 設定と共にアプリ全体を workerd 内で実行します。 より重くなりますが、完全なランタイムです。 エントリは wrangler 設定の `main` なので、ファイル引数は渡さないでください。
#### Vite
`--runtime vite` は Vite プラグインがビルドするアプリ向けに、プロジェクトの Vite 開発サーバーを通してリクエストを送信します。 アプリは Vite 設定から取得されるため、ファイル引数は渡さないでください。 `cloudflare.config.ts` と Vite 設定があるプロジェクトでは `c.env` にバインディングがあり、デフォルトになります。 また `src/index.ts` がない Vite プロジェクトでもデフォルトです。 これは `request`, `batch`, `snapshot` で動作します。 ファイル引数、`--no-bindings`, `--trace`, `--watch` を指定すると、代わりに Node.js 上でアプリが実行されます。
### batch
JSONL から複数のリクエストを一回の呼び出しで順番に、一つのアプリインスタンスに対して実行します。 メモリ内状態はステップ間で引き継がれます。
```sh
hono batch - <<'JSONL'
{"path":"/users","expect":{"status":200}}
{"method":"POST","path":"/users","body":{"name":"Momo"},"expect":{"status":201,"body":{"name":"Momo"}},"save":{"id":".id"}}
{"path":"/users/{{id}}","expect":{"status":200}}
{"method":"DELETE","path":"/users/{{id}}","expect":{"status":204}}
JSONL
```
一行に一つの JSON オブジェクト: `method`, `path`, `body`, `headers`, `expect`, `save`。
- `expect` は受け入れ基準を宣言します。 `status` は完全一致です。 `body` は深い部分一致です: 宣言されたフィールドがマッチする必要があり、レスポンスの余分なフィールドは無視されます。
- `save` はドットパスでレスポンスボディから値を保存し、後のステップで `{{id}}` として使用します。
- 文字列の `body` はフォームとして送信されます。 または JSON オブジェクトや配列の場合は JSON として送信されます。 ただし `headers` でコンテンツタイプを設定した場合は除きます。 オブジェクトの `body` は JSON として送信されます。
- Cookie はブラウザのように引き継がれます。 Cookie を設定するログインステップは、その後のステップをサインイン状態にします。 ステップ内の `cookie` ヘッダーが優先されます。
- `expect` のないステップは 2xx または 3xx で合格し、4xx または 5xx で失敗します。 意図的に 4xx を受け入れるには、`expect.status` で宣言してください。
- 失敗したステップは `diff` を持ち、不一致ごとに一行です。
出力には各ステップの実際の `status` と `body`、ステップごとの `pass`、`summary` が含まれます。 `failed` が 0 になるまで再実行してください。
- `-H, --header ` - すべてのステップに共有されるヘッダー
- `--compact` - 失敗したステップとサマリーのみを出力する
- `--runtime ` - `node` (default), `workerd`, or `vite`
- `--no-bindings` - ローカルの Cloudflare バインディングの読み込みをスキップする
### snapshot
アプリの現在の挙動をバッチ JSONL 行として標準出力に出力します。 ファイルは書き込まれません。
```sh
hono snapshot
hono snapshot --status-only src/app.ts
```
パラメータなしの GET ルートが実行され、その実際のステータスとボディが `expect` になります。 パラメータ付きの GET ルートは、アプリがすでにリンクやパラメータ名を持つ JSON フィールドでそのパスを示していた場合に実行されます。 その他のパラメータ付きルートと非 GET ルートは `expect` なしで出力されるため、あなたが記入します。 一つのプローブ行が、どのルートにもマッチしないパスのレスポンスを記録します。
`routes` とは異なり、このコマンドは実際のリクエストを送信するため、ミドルウェアが実行されます。
リファクタリング前にキャプチャし、`failed` が 0 になるまで `hono batch` で行を再実行してください。
- `--status-only` - ボディではなくステータスコードのみをキャプチャする
- `--runtime ` - `node` (default), `workerd`, or `vite`
- `--no-bindings` - ローカルの Cloudflare バインディングの読み込みをスキップする
### diff
未コミットの変更がアプリに何をするかを表示します。 最後のコミット時と現在のアプリを実行し、答えを比較します。 事前のキャプチャは不要です。
```sh
hono diff
hono diff --base main
```
- `--base [` - 比較対象のコミット (デフォルト: `HEAD`)
`hono snapshot` が実行する GET ルートと 404 プローブが両側で実行されます。 `changed` は、ステータスとボディの差異と共に、異なる応答をしたものを一覧にします。 出現または消失したルートは `added` と `removed` に入ります。 その他のパラメータ付きルートと非 GET ルートは実行されないため、`hono batch` で確認してください。
各側は最新のローカルデータを持つ一時的な git worktree で実行されるため、テスト中に書き込んだデータはカウントされません。 `node_modules` は共有され、`.dev.vars` はコピーされます。 リファクタリング後の空の `changed` は、GET ルートが以前と同じ応答をすることを意味します。
```json
{
"ok": true,
"data": {
"base": "HEAD",
"compared": 6,
"changed": [
{ "route": "GET /api/posts", "status": "200 -> 401" }
],
"added": ["POST /login"],
"removed": []
}
}
```
### benchmark
Hono アプリのパフォーマンスを計測します。 ルーティングとハンドラーのマイクロベンチマークです: `app.request()` が HTTP スタックやネットワークなしで直接呼び出されます。 各実行は新しいプロセスで行われるため、結果は比較可能です。
```sh
hono benchmark
hono benchmark -P /users
hono benchmark -P /users -X POST -d '{"name":"Alice"}' -H 'Content-Type: application/json'
hono benchmark --hono 4.12.3 --hono 4.13.0
hono benchmark --hono ../hono
```
- `-P, --path ` - このパスのみをベンチマークする (複数回使用可能)
- `-X`, `-d`, `-H` - `-P` パス用のメソッド、ボディ、ヘッダー
- `--duration ` - 各ルートを計測する時間 (デフォルト: `500`)
- `--warmup ` - 計測前のリクエスト数 (デフォルト: `30`)
- `--hono ` - 別の Hono で同じアプリをベンチマークする: npm バージョン、またはローカルチェックアウト
数パーセントの差はノイズです。 比較するには、複数回実行し、差が繰り返されるか確認してください。
### ssg
Hono アプリから静的ファイルを生成します。 [SSG ヘルパー](/docs/helpers/ssg) のように。
```sh
hono ssg
hono ssg -o dist/static src/app.ts
hono ssg --exclude '/api/*'
```
- `-o, --outdir ` - 出力ディレクトリ (デフォルト: `static`)
- `--include ` / `--exclude ` - パスでルートを選択する。 `*` は任意にマッチする
- `--no-bindings` - ローカルの Cloudflare バインディングの読み込みをスキップする
200 を返さないページは書き込まれません。 そのステータスと共に `skipped` に一覧されるため、`hono request ` で確認してください。
```json
{
"ok": true,
"data": {
"output": "static",
"files": ["static/index.html", "static/about.html"],
"skipped": [{ "path": "/counter", "status": 500 }]
}
}
```
### init
[create-hono](/docs/guides/create-hono) テンプレートから Hono アプリを現在のディレクトリに追加します。 `npm create hono` が使えない、すでに存在するプロジェクトで使用します。
```sh
npm install -D @hono/cli
npx hono init --template cloudflare-workers --install
npx hono request /
```
- `-t, --template ` - テンプレート。 `aws-lambda`, `bun`, `cloudflare-workers`, `deno`, `fastly`, `lambda-edge`, `netlify`, `nextjs`, `nodejs`, `vercel`, `x-basic` のいずれか
- `-i, --install` - ロックファイルから、または `hono` を起動したコマンドからパッケージマネージャーで依存関係をインストールする
`--template` なしでは、テンプレートはディレクトリ内のファイルから決まります: `wrangler.jsonc` または `cloudflare.config.ts` は `cloudflare-workers`、`deno.json` は `deno`、`bun.lock` は `bun`、などです。 ヒントがない場合、ターミナルには選択リストが表示されます。 ターミナルがない場合、エラーがテンプレートを一覧します。
すでに存在するファイルは決して上書きされません。 それは `skipped` に一覧されます。 `package.json` はマージされ、既存の値が優先されますが、`type` はテンプレートが `module` に設定します。 wrangler 設定がある場合、テンプレートは `cloudflare.config.ts` を追加しません。 逆も同様です。 `--install` なしでは、次にパッケージマネージャーを実行してください。
```json
{
"ok": true,
"data": {
"template": "cloudflare-workers",
"detectedFrom": "wrangler.jsonc",
"written": ["src/index.ts"],
"skipped": ["wrangler.jsonc"]
}
}
```
# コーディングエージェント向けの Hono
Hono はコーディングエージェントに適しています。 API は小さく、 Web 標準の上に構築されているため、エージェントはそのほとんどをすでに知っています。 このページでは、エージェントが Hono でうまく動作するために私たちが提供するものを一覧にしています。
## はじめに
プロジェクトを作成し、その中でエージェントを実行してください。
```sh
npm create hono@next my-app
```
テンプレートには、エージェントが必要とするものが含まれています。 アプリの確認方法をエージェントに伝える `AGENTS.md` と、開発依存関係としての [Hono CLI](/docs/agents/cli) です。 それ以外のセットアップは必要ありません。
## エージェント向けのドキュメント
このサイトのすべてのページは Markdown として利用できます。 `Accept: text/markdown` ヘッダーで取得してください:
```sh
curl -H 'Accept: text/markdown' https://hono.dev/docs/api/routing
```
適切なページを見つけるには、 [`/llms.txt`](/llms.txt) から始めてください。 すべてのページを1行の説明付きで一覧しています。 [`/llms-full.txt`](/llms-full.txt) にはドキュメント全体が1つのファイルに含まれており、 [`/llms-small.txt`](/llms-small.txt) はより短いバージョンです。
## Hono スキル
[Hono Skills](https://github.com/honojs/skills) は Hono 用の [Agent Skills](https://agentskills.io) です。 `hono` スキルは、エージェントにインラインの API リファレンスと Hono CLI でリクエストをテストする方法を提供します。 `hono-jsx` スキルは `hono/jsx` を使った UI をカバーします。
[skills.sh](https://skills.sh) または [GitHub CLI](https://cli.github.com) でインストールしてください。 どちらも、エージェントが読み取る場所にスキルを配置します。
```sh
# skills.sh
npx skills add honojs/skills
# GitHub CLI
gh skill install honojs/skills hono
gh skill install honojs/skills hono-jsx
```
## Hono CLI
[Hono CLI](/docs/agents/cli) はアプリを直接読み込むため、エージェントはサーバーを起動せずに検査やテストができます。 すべてのコマンドは JSON を出力します。
```sh
hono routes # all routes, without reading the source
hono request /users # one request, through app.request()
hono batch - # many requests with expectations, until "failed": 0
hono snapshot # the current behavior, as batch lines
hono diff # what changed vs the last commit
```
`hono --help` はエージェント向けの注記から始まり、 `hono --help` には例が含まれています。 それ以外のセットアップは必要ありません。
## AGENTS.md
エージェントは、プロジェクトが実行や確認の方法として指定した方法に従います。 `AGENTS.md` に1行書くだけで、開発サーバーの代わりに Hono CLI を使わせることができます。 残りは `hono --help` から学習します。 create-hono のテンプレートにはすでに含まれています。
```md
## Hono CLI
Run `npx hono --help` first, and use the CLI instead of a dev server.
```
または、エージェントに直接伝えてください。 同じ1行がプロンプトとしても機能します。
## エージェントは Hono をどの程度うまく使えますか?
私たちはそれを計測しています。 結果は [Agent DX](https://agent-dx.hono.dev) をご覧ください。
]