ミドルウェア
すべてのクライアントサイドナビゲーションの前に実行されるルートガード。
src/middleware.ts は、すべてのナビゲーション — 初回マウント、push/replace、ブラ
ウザの戻る/進む — の前に実行され、それをリダイレクトできるデフォルト関数をエクスポート
できます:
import type { Middleware } from 'murasaki'
const middleware: Middleware = ({ pathname }) => {
if (pathname === '/admin') return { redirect: '/' }
}
export default middleware<AppRouter>(スキャフォールドのエントリがマウントします)は、この関数を仮想ルートテ
ーブルから自動的に読み込みます — 手動で登録する必要はなく、このファイルが存在しなくて
も何も起こりません。
シグネチャ
export interface MiddlewareContext {
pathname: string
}
export type MiddlewareResult = { redirect: string } | void | undefined
export type Middleware = (
ctx: MiddlewareContext,
) => MiddlewareResult | Promise<MiddlewareResult>ctx は意図的に小さく保たれています — ナビゲーション先の pathname だけです。
request/response もヘッダーも cookie もありません — これは完全にクライアントサイ
ドで、あなたのルートと同じ WebView の中で実行され、エッジや server ランタイムではあり
ません。リダイレクトせずに早期リターンするには、何も返さない(undefined)だけです。
非同期でも構いません — <AppRouter> はマッチしたルートをレンダーする前にそれを await
し、その間は何もレンダーしないため、ガードされたルートが一瞬でも表示されることはありま
せん。
ルートをガードする
もっとも一般的な形は、クライアントサイドの状態(ストア、localStorage、あるいは
document.cookie 経由の cookie)を読んで、未認証の訪問者を保護されたパスから遠ざける
認証チェックです:
import type { Middleware } from 'murasaki'
import { useAuth } from '@/lib/auth'
const PROTECTED = ['/settings', '/billing']
const middleware: Middleware = ({ pathname }) => {
const isProtected = PROTECTED.some((p) => pathname.startsWith(p))
if (isProtected && !useAuth.getState().isSignedIn) {
return { redirect: '/login' }
}
}
export default middlewareリダイレクトなしの副作用
middleware は必ずしもリダイレクトする必要はありません — void を返せばナビゲーショ
ンはそのまま通過するため、すべてのナビゲーションで副作用を実行する場所(例えば分析用の
ページビュー計測)としても便利です:
import type { Middleware } from 'murasaki'
import { trackPageview } from '@/lib/analytics'
const middleware: Middleware = ({ pathname }) => {
trackPageview(pathname)
}
export default middlewareリダイレクトループ(5回以上連続したリダイレクト)は自動的に検出されます: Murasaki は 警告をログに出力し、ハングする代わりに現在のパスをそのままレンダーします。
どのパスでミドルウェアを実行するかを絞り込む matcher/config のようなエクスポート
はありません — Next.js と異なり、middleware は常にすべてのナビゲーションで実行され
るため、上の例のように関数の中で pathname に応じて自分で分岐します。
これは Next.js のミドルウェアに相当する Murasaki のクライアントサイド版です — あなたのルートと同じ WebView の中で実行されるため、エッジランタイムは関与しません。