runlot
はじめかた

フレームワーク

Hono と静的サイトはアダプターなしでデプロイでき、Next.js は @runlot/next アダプターがビルドを処理します。

runlot の基本的なデプロイ単位は、ワーカーと静的アセットのディレクトリです。フレームワークはこの構成の上で動作します。

Hono

アダプターは必要ありません。Hono アプリは fetch ハンドラーを提供するため、export default app とエクスポートするだけで済みます。

src/index.ts
import { Hono } from "hono";
import { Pool } from "@runlot/pg";

const app = new Hono<{ Bindings: Env }>();

app.get("/posts", async (c) => {
  const user = await c.env.auth.user(c.req.raw);
  const db = new Pool({ db: c.env.db });
  const { rows } = await db.query(
    "select id, title from posts where owner = $1 limit 20",
    [user?.id ?? null],
  );
  return c.json(rows);
});

export default app;
runlot.json
{ "name": "my-app", "main": "src/index.ts", "assets": "public" }

静的サイト

main を指定せず assets だけを指定すると、静的サイトとしてデプロイします。

runlot.json
{ "name": "my-site", "assets": "dist" }

/ と拡張子のないパスは index.html で応答し、Content-Type は拡張子から決まります。React Router のようなクライアントルーティングを使う場合は "notFound": "spa" を追加してください — アセットにない拡張子なしのパスが /index.html で応答します (wrangler の not_found_handling: "single-page-application" と同じ位置づけです)。

runlot.json
{ "name": "my-site", "assets": "dist", "notFound": "spa" }

Vite、Astro、11ty のようにビルド工程がある静的サイトは、ビルドしたあとに出力ディレクトリを assets に指定してください。

npm run build
runlot deploy

静的サイトは、静的アセットだけを含む runlot のデプロイです。あとからワーカーのコードを追加しても、サービスを移すことなく同じプロジェクトからデプロイを続けられます。

Next.js

npm create runlot@latestnext テンプレートを選択すると、以下の構成が生成されます。実際のランタイム環境での検証は進行中です。最初のデプロイで問題が発生した場合はお知らせください。

runlot.json"framework": "next" を指定すると、runlot deploynext build と OpenNext のビルドを実行します。mainassets はビルド時に生成されるため、自分で指定する必要はありません。プロジェクトには next@runlot/next がインストールされている必要があります。

runlot.json
{ "name": "my-app", "framework": "next" }

Next.js のコードでは、リクエストオブジェクトを直接渡さなくてよい env を使用します。アダプターが現在のリクエストのコンテキストを処理します。

app/page.tsx
import { env } from "@runlot/next";
import { Pool } from "@runlot/pg";

export default async function Page() {
  const user = await env.auth.user();
  const db = new Pool({ db: env.db });
  const { rows } = await db.query("select id, title from posts limit 20");
  return <main>{user?.email} · {rows.length}</main>;
}

env はリクエストを処理している間だけ利用できます。モジュールのトップレベルや、ビルド時の静的レンダリングで読み取るとエラーになります。

ISR は env.storage を、タグの再検証は env.db を使用します。runlot.json"database": true"storage": true を書いてください。追加しない場合は静的アセットのキャッシュが使われ、デプロイのログに案内が表示されます。別途の設定ファイル(wrangler.toml)は作成しません。.open-next/.runlot-next/ はビルド成果物のため、テンプレートの .gitignore にすでに含まれています。

next/image の画像最適化は現在サポートしていません。画像は元のファイルのまま配信されます。

フレームワークが自分でワーカーをビルドする場合

runlot はフレームワークのビルドを代わりに実行しません。runlot deploymain が指すファイルを esbuild でまとめるため、virtual:react-router/server-build のようなビルド時の仮想モジュールは、ソースのエントリーポイントからは解決できません。

先にビルドを実行し、main がその成果物を指すようにしてください。サーバービルドが fetch をエクスポートする 1 つの ESM ファイルとして出るフレームワークは、私たちのアダプターなしでデプロイできます。React Router の Cloudflare ターゲットがちょうどその形です。

npm run build
runlot.json
{ "name": "my-app", "main": "build/server/index.js", "assets": "build/client" }

wrangler と違う点が 1 つあります。main がある場合、ワーカーがエントリーポイントであり、静的アセットはその手前では配信されません。 リクエストをフレームワークに渡す前に、ワーカーから直接配信してください。詳しくは 静的アセット を参照してください。

const url = new URL(request.url);
if (url.pathname.startsWith("/assets/")) return env.assets.fetch(request);
return handler(request);
React Router の SSR サーバービルドがデプロイのバンドラーをそのまま通ることは確認しました。デプロイの全区間の検証はまだです。問題があればお知らせください。

SvelteKit と Nuxt は、この形を対象とするアダプターが必要ですが、まだありません。クライアントビルドのみを提供する構成 (SPA と別建ての API ワーカー) は、静的サイトの方式でデプロイできます。

定期実行

runlot.jsontriggers を書くと、ワーカーの scheduled ハンドラがその時刻に動きます — 定期実行 を参照してください。

runlot.json
{ "name": "my-app", "main": "src/index.ts", "triggers": { "crons": ["30 18 * * *"] } }

このページの目次