ルーティングガイド
Guren は Hono の HTTP サーバー上に Laravel 風のルーティング DSL を提供します。推奨構成では routes/web.ts が registrar 関数を export し、アプリ起動時に app-local な Router へルートを登録します。
ルーティングガイド
Guren は Hono の HTTP サーバー上に Laravel 風のルーティング DSL を提供します。推奨構成では routes/web.ts が registrar 関数を export し、アプリ起動時に app-local な Router へルートを登録します。
基本の使い方
routes/web.ts を作成または編集し、Router と使用するコントローラーをインポートします。
import { Router } from '@guren/core'
import PostsController from '@/app/Http/Controllers/PostsController'
export function registerWebRoutes(router: Router): void {
router.get('/', [PostsController, 'index'])
router.post('/posts', [PostsController, 'store'])
}
各ルートはパスと以下のいずれかを受け取ります。
- コントローラータプル
[ControllerClass, 'method'] - インラインハンドラー
(ctx) => new Response('...')
利用できるメソッドは router.get、router.post、router.put、router.patch、router.delete、そして汎用の router.on(method, path, handler) です。
ルートグループ
router.group(prefix, callback) を使って、共通のパスプレフィックスとミドルウェアを適用できます。
router.group('/posts', (posts) => {
posts.get('/', [PostsController, 'index'])
posts.get('/:id', [PostsController, 'show'])
})
グループはネスト可能です。プレフィックスは自動的にトリミングされるため、/posts + /new は /posts/new になります。
ミドルウェア
ルート単位のミドルウェア
ハンドラーに .middleware() をチェーンして適用できます。
import { Router, requireAuthenticated } from '@guren/core'
import { requireAdmin } from '@/app/Http/middleware/admin'
export function registerWebRoutes(router: Router): void {
router.aliasMiddleware('auth', requireAuthenticated())
router.aliasMiddleware('admin', requireAdmin())
router.get('/admin', [AdminController, 'index']).middleware('auth', 'admin')
}
ミドルウェアエイリアス
ミドルウェア関数に短い名前を登録しておけば、ルート全体で文字列で参照できます。
import { Router, requireAuthenticated } from '@guren/core'
import { requireAdmin } from '@/app/Http/middleware/admin'
export function registerWebRoutes(router: Router): void {
router.aliasMiddleware('auth', requireAuthenticated())
router.aliasMiddleware('admin', requireAdmin())
}
エイリアスを登録すれば、ミドルウェアが受け入れられる場所ならどこでも文字列名で使えます。
router.get('/dashboard', [DashboardController, 'index']).middleware('auth')
router.get('/admin', [AdminController, 'index']).middleware('auth', 'admin')
ミドルウェアグループ
よく使うミドルウェアの組み合わせを一つの名前にまとめられます。
router.groupMiddleware('web', ['session', 'csrf'])
router.groupMiddleware('api', ['throttle:60'])
ミドルウェアグループをルートグループに適用します。
router.middleware('web').group((web) => {
web.get('/', [HomeController, 'index'])
web.get('/about', [PagesController, 'about'])
})
router.middleware('auth').group((auth) => {
auth.get('/dashboard', [DashboardController, 'index'])
auth.get('/settings', [SettingsController, 'index'])
})
ミドルウェアグループと個別のエイリアスは自由に組み合わせられます。
router.middleware('web', 'auth').group((group) => {
group.get('/profile', [ProfileController, 'show'])
})
グローバル登録パターン、ビルトインヘルパー、セッションサポートの詳細は、専用のミドルウェアガイドをご覧ください。
ルートパラメータ
動的パラメータは Hono の構文に従います。
router.get('/posts/:id', [PostsController, 'show'])
コントローラー内では this.validateParams() か this.ctx.req.param('id') でパラメータを読み取ります。
オプショナルセグメントには Hono のパターンサポート(router.get('/posts/:id?', handler))を使い、ワイルドカードには *(例: /:slug*)を使います。
ルートモデルバインディング
毎回 findOrFail() を書きたくない場合は、ルートパラメータにモデルをバインドできます。
import { PostResource } from '@/app/Http/Resources/PostResource'
import { pages } from '@/.guren/pages.gen'
router.bind('post', Post)
router.get('/posts/:post', [PostsController, 'show'])
async show() {
const post = this.ctx.get('post') as PostRecord
return this.inertia(pages.posts.Show, { post: new PostResource(post).toJSON() })
}
slug ベースの解決にしたい場合は、独自 resolver も渡せます。
router.bind('post', async (value) => Post.where('slug', value).firstOrFail())
ブートストラップ
src/app.ts で registrar を createApp() に渡します。
// src/app.ts
import { createApp } from '@guren/core'
import registerWebRoutes from '@/routes/web'
const app = createApp({
routes: registerWebRoutes,
})
カスタムハンドラー
インラインハンドラーを使えば、コントローラーなしで Hono の Context を直接扱えます。
router.get('/health', (ctx) => ctx.json({ ok: true }))
ヘルスチェックや Webhook のような軽量エンドポイントに便利です。
Tips
routes/web.tsは HTTP 定義に集中させましょう。ビジネスロジックはコントローラーやサービスに移してください。- 大規模なアプリでは、ルートを追加ファイル(例:
routes/admin.ts)に分割し、src/app.tsで registrar を合成します。 - 分かりやすいコントローラーメソッド名(
index、show、store、update、destroy)を使うと、フレームワーク全体の規約と揃います。 - ミドルウェアエイリアスを活用すると、ルートファイルがすっきりし、あちこちでミドルウェア関数をインポートする必要がなくなります。
ルーティング DSL を使えば、複雑な HTTP 構造を表現しながら、エントリーポイントをクリーンで宣言的に保てます。
ルートコントラクト
第 2 引数にオプションオブジェクトを渡すと、Zod スキーマとメタデータをルートに付与できます。フレームワークはこれらのスキーマをリクエストバリデーション、コード生成、OpenAPI ドキュメント生成に使用します。
import { z } from 'zod'
const CreatePostSchema = z.object({
title: z.string().min(1),
body: z.string().min(1),
})
const PostIdParams = z.object({
id: z.coerce.number().int().positive(),
})
router.post('/posts', {
body: CreatePostSchema,
name: 'posts.store',
}, [PostsController, 'store'])
router.get('/posts/:id', {
params: PostIdParams,
name: 'posts.show',
}, [PostsController, 'show'])
利用可能なコントラクトフィールド:
| フィールド | 用途 |
|---|---|
name |
URL 生成・コード生成用のルート名 |
params |
パスパラメータの Zod スキーマ |
query |
クエリパラメータの Zod スキーマ |
body |
リクエストボディの Zod スキーマ |
output |
レスポンスボディの Zod スキーマ |
bind |
ルートモデルバインディングマップ |
middlewares |
ミドルウェアハンドラーの配列 |
Note
同じクエリキーが繰り返された場合、query スキーマには配列として渡されます(?tag=a&tag=b → { tag: ['a', 'b'] })。1 回だけ出現するキーは文字列のままです。詳細は配列形式のクエリパラメータを参照してください。
OpenAPI メタデータ
ルートコントラクトには軽量な OpenAPI アノテーションも指定できます。これらはルート定義に保存され、オプションの @guren/openapi プラグインで OpenAPI 3.1 ドキュメントを生成する際に使用されます。
router.post('/posts', {
body: CreatePostSchema,
output: PostResponseSchema,
name: 'posts.store',
summary: 'Create a post',
description: 'Creates a new blog post.',
tags: ['Posts'],
}, [PostsController, 'store'])
router.get('/posts/:id', {
params: PostIdParams,
name: 'posts.show',
summary: 'Get a post',
tags: ['Posts'],
deprecated: false,
}, [PostsController, 'show'])
利用可能な OpenAPI フィールド:
| フィールド | 型 | 用途 |
|---|---|---|
summary |
string |
ドキュメント UI に表示される短い説明 |
description |
string |
エンドポイントの詳細な説明 |
tags |
string[] |
ドキュメント UI でエンドポイントをグループ化 |
operationId |
string |
自動生成されるオペレーション ID を上書き |
deprecated |
boolean |
エンドポイントを非推奨としてマーク |
スペックドキュメントの生成については CLI リファレンスの OpenAPI セクションを参照してください。
OpenAPI ドキュメント生成
オプションの @guren/openapi パッケージをインストールして、ルート定義からスペックを生成します。
bun add @guren/openapi
bunx guren openapi:generate
ルートファイルを読み取り、ルートコントラクトから Zod スキーマと OpenAPI メタデータを抽出し、OpenAPI 3.1 JSON ドキュメントを .guren/openapi.gen.json に書き出します。
CLI オプション
# タイトルとバージョンを指定
bunx guren openapi:generate --title "Blog API" --version "1.0.0"
# 出力パスを変更
bunx guren openapi:generate --out docs/openapi.json
# サーバー URL を含める
bunx guren openapi:generate --server "https://api.example.com"
# 既存ファイルを上書き
bunx guren openapi:generate --force
ランタイムでのドキュメントマウント
OpenAPI スペックとインタラクティブなドキュメント UI をアプリケーションから直接配信することもできます。
import { createApp } from '@guren/core'
import { mountOpenApiDocs } from '@guren/openapi'
const app = createApp({ routes: registerWebRoutes })
mountOpenApiDocs(app, {
title: 'Blog API',
version: '1.0.0',
})
以下の 2 つのエンドポイントがマウントされます。
| パス | 説明 |
|---|---|
/openapi.json |
生成された OpenAPI 3.1 JSON ドキュメント |
/docs |
インタラクティブな API ドキュメント UI(Scalar) |
パスは jsonPath と docsPath オプションでカスタマイズできます。
mountOpenApiDocs(app, {
title: 'Blog API',
version: '1.0.0',
jsonPath: '/api/openapi.json',
docsPath: '/api/docs',
})
Application インスタンスにマウントする場合、ルート定義はルーターから自動的に読み取られます。素の Hono インスタンスの場合は definitions を明示的に渡してください。