対象読者
- Reactの基本を修めている方
- 通信回線が弱いユーザーにも高速に表示できるサイトを作りたいエンジニア
- WebブラウザとNode.jsという異なるランタイムをそれぞれキャッチアップするのが辛くなってきたエンジニア
前提環境
筆者の検証環境は以下の通りです。
- macOS Sonoma 14.5
- Node.js 22.4.1
- NPM 10.8.1
- Remix 2.9.2
プロジェクトの構成を確認する
さて、前回の手順でプロジェクトが作成できましたが、通常のRemixプロジェクトとは少し異なる部分があります。簡単におさらいしていきましょう。図1では、前回作成したRemixプロジェクトの構成を示しています。通常のnpm create remix@latest
というコマンドで作成したプロジェクトと比べて、追加されたファイルにピンクの下線を引き、大きな変更があったファイルには青い囲みを付けています。
それぞれのファイルの役割や変更点を上から順に簡単に解説します。
app/entry.server.tsx
app/entry.server.tsx
は、サーバーサイドレンダリング処理のエントリーポイントです。サーバーが受け取ったリクエストに対して、レスポンスを構築するための入り口となるファイルのことですね。
このファイルには大きな変更がありました。Node.js向けにnpm create remix@latest
で作成した場合、このentry.server.tsx
のテンプレートは140行程度です。一方、Cloudflare向けのテンプレートでは40行程度に収まっています。これは、RemixサーバーがWeb標準のストリームAPIのインターフェースであるReadableStream型のデータをレスポンスボディとして返しているために起きた差ではないかと思われます。
Cloudflare Workersの処理系は、ブラウザのService WorkerをCDNのエッジサーバーで動かしているため、ブラウザと同じようにストリームAPIを扱うことができます。一方、Node.jsのストリームAPIはv16で初登場し、長らくExperimental(実験的な機能)扱いだったのが、v21でStable(安定版)になりました。
RemixではNode.js v21未満のバージョンもサポートしているため、2024年現在は安定版ではないストリームAPIには頼らない形でentry.server.tsx
を実装しているものと思われます。いつか、Node.jsの古いバージョンをサポートしなくなった頃に、Node.js向けのテンプレートもReadableStreamを前提としたものに変わるのかもしれません。
functions/[[path]].ts
functions/[[path]].ts
は、Page Functionsのエントリーポイントです。Webサイトにアクセスがあったときに、URLのパスに対応するパスのファイルがfunctions
内にあった場合、そのファイルが実行されます。たとえば、/about
にアクセスがあった場合には、functions/about.ts
が実行されるわけですね。
今回のように[[abc]].ts
のような[[
と]]
で囲まれた部分は、ワイルドカードとして扱われ、任意のパスにマッチします。つまり、functions/[[path]].ts
は、どんなパスにもマッチするエントリーポイントになるのです。詳しくは、Page Functionsのドキュメントを参照してください。
内容はシンプルで、Remixのハンドラに処理を委譲しているだけです。Remixフレームワーク内の流れとしては、まずはfunctions/[[path]].ts
がすべてのリクエストを受け付けて、Remixのハンドラを通じてapp/entry.server.tsx
に具体的な処理を委譲する形になっています。
public/_headers
public/_headers
はCloudflare Pages固有の設定ファイルです。所定のパスにアクセスがあったときに、デフォルトで付与するヘッダー情報を記述することができます。今回のケースでは、リスト1のように設定されています。
/favicon.ico Cache-Control: public, max-age=3600, s-maxage=3600 /assets/* Cache-Control: public, max-age=31536000, immutable
/favicon.ico
のキャッシュを1時間に設定するとともに、/assets/*
以下のファイルは1年間キャッシュするように設定されています。/assets
は見慣れないパスかもしれませんが、npm run build
コマンドでビルドしたときに生成される.js
ファイルや.css
ファイルが格納される、build/client/assets
フォルダを指しています。Cloudflare Pagesには/build/client
フォルダをルートディレクトリとして静的ホスティングするので、デプロイ後にキャッシュ対象とするURLは/assets/*
になるわけです。
public/_routes.json
public/_routes.json
はPage Functions固有の設定ファイルです。リクエストがあったときにfunctions/
配下の処理を呼び出すかどうかを設定することができます。リスト2のように設定されています。
{ "version": 1, "include": ["/*"], "exclude": ["/favicon.ico", "/assets/*"] }
静的配信したい(そして_headers
でキャッシュしている)ファイルへのアクセスではPage Functionsが呼び出されないようにexclude
を設定しつつ、それ以外のすべてのリクエストに対してPage Functionsを呼び出すようにinclude
を設定しています。
load-context.ts
load-context.ts
は、Remixのloaderなどでアクセスできるcontext
オブジェクトを定義・拡張するためのファイルです。今回のケースでは、context.cloudflare
を追加しています。Cloudflareの各種APIを利用するため、TypeScriptの型定義の拡張を行なうことが目的です。
context
をさらに細かく拡張したい場合は、公式ドキュメントを参照して、getLoadContext
関数を定義するとよいでしょう。
package.json
package.json
では@remix-run/node
が@remix-run/cloudflare
と@remix-run/cloudflare-pages
に置き換えられたのが大きな変更です。また、Cloudflare Workersをはじめとした開発者向けサービスの管理用CLIツールである、Wrangler(ラングラー)も新たにインストールされ、NPM ScriptsにもWranglerを使ったデプロイコマンドが追加されています。
vite.config.ts
vite.config.ts
には、小さいようで大きな変更として、@remix-run/dev
に含まれるcloudflareDevProxyVitePlugin
というプラグインが設定されています。これは、開発時にローカル環境を立ち上げるタイミングで、Wranglerと連携するためのプラグインです。
本来、Cloudflare Workersの実行環境はNode.jsとは異なるため、ローカル環境では動作確認が困難ですが、WranglerにはMiniflare(ミニフレア)という、ローカル環境でCloudflare Workersをエミュレートするためのツールが内蔵されています。
cloudflareDevProxyVitePlugin
を通じて、Viteの開発サーバーとMiniflareを連携させることで、ローカル環境でCloudflare PagesやPage Functionsの動作をエミュレートできるようになっているのです。
wrangler.toml
説明の都合で順番が前後しますが、wrangler.toml
について先に解説させてください。wrangler.toml
は、Wranglerコマンドの設定ファイルでもあり、Cloudflare WorkersやCloudflare Pagesによってのプロジェクト管理ファイルでもあります。リスト3のように設定されています。
#:schema node_modules/wrangler/config-schema.json name = "remix-on-cloudflare-sample" compatibility_date = "2024-06-14" pages_build_output_dir = "./build/client"
name
はプロジェクト名を指しており、このままデプロイすれば、Cloudflare Pagesのプロジェクトがこの名前で作成され、pages.dev
のサブドメインとして公開されることになります。「remix-on-cloudflare-sample」という名前は本記事で筆者がとってしまったので、読者の皆さんが試すときは別の名前に書き換えてもよいかもしれません。
compatibility_date
は、Wranglerのバージョンとの互換性を指定するためのフィールドです。pages_build_output_dir
は、ビルドしたファイルが格納されるディレクトリを指定しています。Cloudflare Pagesは、このディレクトリをルートディレクトリとして静的ホスティングするため、Remixのビルド先のディレクトリを指定しています。
また、wrangler.toml
には、Cloudflare Workers向けの各種サービスを紐づけるための設定を記述する役割もあります。たとえば、SQLiteデータベースのサービスであるCloudflare D1を利用する場合は、リスト4のように設定します。
# (省略) [[d1_databases]] binding = "MY_DB" database_name = "my-database" database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
[[d1_databases]]
は、Cloudflare D1のデータベースを指定するためのセクションです。binding
は、WorkersやPage Functionsからアクセスするための変数名を指定します。database_name
は、事前にセットアップ済みのCloudflare D1のデータベース名を指定します。database_id
は、database_name
に対応する、Cloudflare D1のデータベースIDを指定します。このように、wrangler.toml
には、Cloudflareの各種APIを利用するための設定を記述することができます。
同様に、ストレージサービスのR2や、Workers AIといった、Cloudflareの各種APIを利用する際には、wrangler.toml
に設定を追加していくことになります。
worker-configuration.d.ts
最後に、worker-configuration.d.ts
は、Wranglerによって生成される、Cloudflare Workersの各種APIを利用するためのTypeScript型定義ファイルです。wrangler types
コマンドで生成されます。
リスト4の例で解説してみましょう。wrangler.toml
にbinding = "MY_DB"
を書いただけでは、TypeScriptからはどこにAPIがあるのかわかりませんね。そこで、wrangler types
コマンドを実行すると、Cloudflare D1のAPIにアクセスするための型定義ファイルが生成されます。実際にリスト4のように設定して、wrangler types
コマンドを実行したときの結果を見てみましょう(リスト5)。
$ npm run typegen > typegen > wrangler types ⛅️ wrangler 3.57.1 (update available 3.60.3) ------------------------------------------------------- interface Env { MY_DB: D1Database; }
NPM Scriptsにtypegen
が追加されているので、こちらを実行しました。worker-configuration.d.ts
を見ると、MY_DB
という変数が追加されており、D1Database
型であることがわかります(リスト6)。
// Generated by Wrangler on Sat Jun 15 2024 18:39:59 GMT+0900 (日本標準時)
// by runningwrangler types
interface Env {
MY_DB: D1Database;
}
ここで定義されたEnv
インターフェースは、load-context.ts
で参照され、context
の型を拡張するために使われます。この設定を終えると、loaderでリスト7のようなコードが書けるようになります。
import { LoaderFunctionArgs } from "@remix-run/cloudflare"; export function loader({ context }: LoaderFunctionArgs) { const db = context.cloudflare.env.MY_DB; // (省略) }
これによって、TypeScriptからCloudflareの各種APIを利用する際にも、型安全なコードを書くことができるようになります。
このように、Create Cloudflare CLIを使うことで、Cloudflareの力を最大限活用するための設定が行われたRemixテンプレートを利用できます。npm create remix@latest
で作成したプロジェクトを自分で設定していくのもアリですが、テンプレートの力を借りることで、Cloudflare社のおすすめ設定を取り込めるので、初めての方にはおすすめです。