@k8ordo/static

仕組み

@k8ordo/staticと@k8ordo/serverがどう動いているかを説明します。使い方を覚えるのに必要な内容ではありませんが、ビルドがなぜそのファイルを断るのかが分かると、迷ったときに判断しやすくなります。

このページの内容

モードは依存で決まる

アプリをファイルに書き出すか、サーバーで動かすかは、設定の値ではなく、どちらのパッケージをインストールしたかで決まります。@k8ordo/staticで作ったアプリからは、サーバーの仕組みにそもそも手が届きません。

1つのパッケージのオプションにしなかったのは、このためです。静的なアプリでリクエストを読まないことは、守るべき決まりではありません。読む先のリクエストが、そもそも無いからです。

それ以外は何も変わりません。ルートの書き方も、サーバーとブラウザの境界も、リクエストに答えるハンドラも同じです。プラグインの名前がどちらもframework()なのはそのためで、vite.config.tsはどちらのモードでも同じ形になります。

同じハンドラを、いつ呼ぶかだけが違う

どちらのモードでも、vite buildはdist/rsc/index.jsにリクエストハンドラを書き出します。Requestを受け取ってResponseを返す、ただの関数です。2つのモードの違いは、このハンドラをいつ呼ぶかにあります。

text
@k8ordo/static
  vite build → handler(/products/1)           → index.html
             → handler(/products/1/index.rsc) → index.rsc

@k8ordo/server
  request    → handler(request)               → Response

@k8ordo/staticは、ビルドの最後にページごとにハンドラを2回呼びます。HTMLの答えをindex.htmlに、ペイロードの答えをindex.rscに書き、ファイルとして残します。

@k8ordo/serverは、リクエストが届くたびにハンドラを呼び、その答えをそのまま返します。

vite devは、どちらのモードでもリクエストのたびにハンドラを呼びます。そのため@k8ordo/staticのアプリでは、開発中とビルドで振る舞いが違うところがあります。

ページはHTMLとペイロードの2つの形を持つ

どちらのモードでも、ページにはHTMLのほかに、RSCのペイロードという形があります。ペイロードはページのパスの後ろに/index.rscを付けた場所にあり、クライアント側の遷移ではこれを取りに行きます。

ヘッダーやクエリではなくパスで分けているのは、静的ホスティングがそのどちらでも答えを変えないからです。パスなら、ファイルの集まりでも動いているサーバーでも、同じ決まりで答えられます。

最初に開いたページでは、描画に使ったペイロードがHTMLに埋め込まれて届きます。hydrationはそれを読むので、同じページを2回取りに行くことはありません。

@k8ordo/staticが断るもの

ファイルを書き出すビルドには、リクエストがありません。そのため、リクエストを必要とするものはビルドが名指しで断ります。

  • 'use server'のモジュール:ファイルはフォームの送信を受け取れません。
  • guard.ts:ファイルには、通すかどうかを決めるリクエストがありません。
  • GET以外のメソッドをexportするroute.ts:ファイルはGETにしか答えられません。
  • searchをexportするページ:ファイルの中身は、searchが何であっても同じです。

vite devは動いているサーバーなので、本来ならこれらも受け付けられます。しかし、開発中は動くのに本番ではどこにも届かないフォームは、最初から動かないフォームよりも困ります。そこでvite devも、そのファイルを読み込んだ時点で同じように断ります。

どの断り方も、this application wants @k8ordo/serverという行で終わります。どれかが必要なら、@k8ordo/serverに移ってください。ほかのコードはそのまま使えます。

パラメータを持つルートの値が分からないときも、ビルドは止まります。警告を出して飛ばすことはしません。ページが半分欠けたサイトを黙って公開するより、止まるほうが安全だからです。

@k8ordo/serverが断るもの

動いているサーバーには、どこからでもリクエストが届きます。そのためハンドラは、アプリが答えるべきでないリクエストを、何も描かないうちに断ります。

  • 別のoriginからのPOST:route.ts以外へのPOSTで、Originヘッダーが無いか、そのホストが一致しないものには403で答えます。別のサイトのフォームに、訪問者のCookieを付けたままアクションを呼ばせないためです。
  • GETとHEAD、POST以外のメソッド:ページへのリクエストなら、その3つをAllowに並べた405で答えます。
  • Viteのbaseの外のURL:アプリのものではないので、404で答えます。

どちらのモードでも断るもの

フレームワークの規約は、覚えておく習慣ではなく、検査できる形にしてあります。URLがどこにあり、サーバーとブラウザの境界がどこにあるかを、読み手の記憶ではなくフレームワークが確かめるためです。次のものは、どちらのモードでもエラーになります。

  • routes/の決まりに合わないファイルやディレクトリと、決して描かれないルート
  • 非同期に検証するparamsSchema
  • クライアントのバンドルに入りそうになった、server-onlyのモジュール
  • ルートから始まるパスではない、Viteのbase
k8ordo

Baselineに入った機能を、制限なく使うReactのライブラリ群。

© 2026 k8o — MIT License

組版:Noto Sans JP / M PLUS 2