@k8ordo/server

Get Started

@k8ordo/server を入れて vite.config.ts にプラグインを 1 つ足し、routes/ にレイアウトとページを置けば、vite build の出力を serve() が動かします。このページはそこまでの最短の道筋と、このモードを選ぶことが何を意味するかを説明します。

モードは依存で決まる

このパッケージを入れることが、アプリを「動くもの」にすることです。リクエストのたびに描画して答えるので、パラメータの値を列挙する必要がなく、知らない URL は本物の 404 になり、フォームは Server Action に届きます。

もう一方の @k8ordo/static は、全ルートをビルド時に描いてファイルを出荷します。両者のあいだでアプリのほかの部分は何も変わりません。ルートの文法も、境界も、リクエストハンドラも同じで、ハンドラがリクエストごとに呼ばれるか、ビルド時にルートごとに呼ばれるかだけが違います。プラグインが両方のパッケージで framework() という名前なのはそのためで、モードを決めるのは import だけです。

インストール

@k8ordo/server は実行時の依存です。デプロイしたアプリが動かすのは serve() とビルドされたハンドラだからです。@k8ordo/server はプラグインで Vite を読みますが、アプリのコードが import する serve()redirect()・型は Vite を読まない @k8ordo/server/runtime から来るので、vite は開発時の依存で足ります。server-only は、サーバー専用のモジュールに付ける import を TypeScript が解決できるようにするためのものです。

npm install @k8ordo/router @k8ordo/server react react-dom server-only
npm install -D vite

ピア依存と、ビルドとサーバーを動かす Node.js の要件です。

  • @k8ordo/router
  • React >= 19.3.0
  • React DOM >= 19.3.0
  • Vite >= 8.2.1
  • Node.js >= 24

vite.config.ts

プラグインは framework() 1 つです。オプションは routesDir(ルートのディレクトリ、既定は src/routes)だけです。

// vite.config.ts
import { framework } from '@k8ordo/server';
import { defineConfig } from 'vite';

export default defineConfig({ plugins: [framework()] });

framework() は Vite のプラグインの配列を返し、その中に React のプラグイン(Fast Refresh)と RSC のパイプラインがすでに入っています。@vitejs/plugin-react を自分で足す必要はありません。

tsconfig.json

型の配線を効かせるには、tsconfig.jsoninclude.k8ordo をグロブで書きます。.k8ordo はドットで始まるので、ディレクトリ名だけを書いた include は黙ってそれを飛ばします。飛ばされてもビルドは通り、href が表に対して検査されなくなるだけです。

{
  "include": ["src/**/*.ts", "src/**/*.tsx", ".k8ordo/**/*.ts"]
}

最小の routes/

ルートレイアウトが <html><body> を描きます。フレームワークは文書のテンプレートを持ちません。見えないテンプレートは、変えられないテンプレートだからです。ディレクティブの無いファイルは Server Component です。

// src/routes/layout.tsx
import type { ReactNode } from 'react';

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}
// src/routes/page.tsx
export default function HomePage() {
  return (
    <>
      <title>home</title>
      <h1>hello</h1>
    </>
  );
}

ルートレイアウトは文書そのもの

hydration は文書の全体を照合します。フレームワークが書いた HTML とブラウザのあいだで、HTML を書き換えるもの、たとえば Web フォントをインライン化したり、メールアドレスを難読化したり、スクリプトを遅延させたりする CDN は、React が照合しようとしている木を変えてしまい、その差分で hydration が失敗します。影響を受けない方法は、そうしたサービスに書き換える余地を与えないことです。アセットは別のオリジンから読まず、同じオリジンから配信します。

動かす

vite dev は本番と同じパイプラインで、Fast Refresh も効きます。vite builddist/ を書き、serve() を呼ぶ小さなスクリプトがそれを動かします。

// serve.js
import { serve } from '@k8ordo/server/runtime';

await serve({ port: 3000 });
vite dev
vite build
node serve.js

生成されるもの

最初の vite devvite build.k8ordo/ を書きます。ルート表(routes.gen.ts)、それを @k8ordo/router に結ぶ型の配線(register.gen.ts)、そして自分自身を git から外す .gitignore です。

vite build は型を検査しません。satisfies による検査の結果を出すのは tsc です。.k8ordo/ は git に入らないので、新しいチェックアウトでは tsc の前に一度 vite devvite build を走らせて書かせます。

中身と、ビルドが拒むものは、リンク先にあります。 routes/

@k8ordo/static を選ぶとき

リクエストを必要とするものが 1 つも無いなら、@k8ordo/static が同じアプリをファイルに書き出し、サーバーを動かし続ける必要はなくなります。次のどれも要らないアプリがそれに当たります。

  • フォームの送信先になる Server Action と、そこからの redirect()
  • ページがリクエストのヘッダーや cookie を読むこと
  • アプリ自身が返す本物の 404
  • 前もって列挙できないパラメータの値

@k8ordo/static

次のステップ