Cloudflare WorkersとNext.jsインテグレーションの問題にOpenNext実装が加わった

Cloudflareが@opennextjs/cloudflareを発表

2024年9月末に開催された「Builder Day 2024」でNext.jsアプリをCloudflare Workersにデプロイする新たな方法が公開された。 この方法は、@opennextjs/cloudflareを使ってビルドする。既存の@cloudflare/next-on-pages方式を置き換える可能性があり、現在は実験的な段階だ。 以下に新旧の開発手順のドキュメントがある。

OpenNextの概要

OpenNextは、Next.jsアプリをVercel以外のインフラにデプロイできるようにするためのツールを開発する団体であり、SST(Serverless Stack)とCloudflareのメンバーがその開発に関わっている。

opennext.js.org

OpenNextというフレームワークが中央にあるわけではなく、単にビルドツールを置くGitHub Organizationが用意されている。 SSTはAWS Lambda向けのビルドを開発していて、Cloudflare側のメンバーはCloudflare Workers向けのビルドを開発している。 Next.jsはセルフホストオプションは最初から用意しているので。 OpenというよりUnVercelというほうが正しい。 とはいえ、Next.jsのVercelへのベンダーロックイン傾向があるという苦言はたびたび開発者たちからも出てきていた(私は賛成していない)。

外部プラットフォームとNext.jsの統合の難しさ

外部プラットフォーマーによるNext.jsアプリのインテグレーションの歴史は死屍累々である。 私が寄付して使っていたServerless Framework系のserverless-next.jsもしばらく更新が滞っているし、AWS公式で似た仕組みを公式に実現していたAmplifyのインテグレーションも何世代か統廃合を繰り返して現在は独自の内部Lambdaリソースにデプロイするようになっている。 FirebaseやAzureも、そしてFastly Compute@Edgeでさえも各社Next.jsアプリをデプロイさせたいがために大量のリソースを投下している。

zenn.dev

zenn.dev

zenn.dev

こうなってしまった要因はもちろんNext.jsが優れたフレームワークで寡占的なシェアを獲得しているからなのだが、プラットフォーマーと開発元が同一である影響からか開発者向けイベントにあわせたビッグバンリリースが多く、そのためにプラグイン開発者たちが変更に追いつけないという事情もある。 それに、Next.js x サーバーレスという領域はフロントエンドとインフラストラクチャーという飛躍した関係性のため、双方の知識を持ってグルーコードが書ける開発者が少ない。 それでも言語特性上Node.js界隈にはそういう開発者は多いのでVercelのような会社は成り立っているのだろうが……

Cloudflareのnext-on-pagesの課題

Cloudflare社も例外ではなく報告されているIssueの状況をみるとnext-on-pagesのメンテナンスには手を焼いていたようだ*1。 そこで彼らがopennextjs実装を進める理由を想像するに、next-on-pages内部で利用しているBuild Output APIが本来Vercelに自分のフレームワークをデプロイするため使う仕様であり、余所のプラットフォームに適合するように変換するための中間形式としては柔軟性に欠けているためではないかと考えられる。

vercel.com

opennextjsの新しいビルドプロセス

next-on-pagesでは、vercel-cliで生成されたBuild Output API (v3)のファイルがCloudflare PagesのFunctionsにデプロイされるというアーキテクチャが用いられており、VercelのデプロイパイプラインがCloudflare向けに再利用される。 一方、opennextjsの実装では、next buildで生成された成果物に直接パッチを当てる。 この際、esbuildを使ってテンプレートから一部の値を注入する戦略は変わらないものの、ビルドプロセスがより直接的になっている。 その後、bin/cloudflareという主語のでかいコマンドが内部で実行され、JavaScriptファイルが.worker-next/以下に展開され、wrangler deployでデプロイされる。

❯ pnpm run;
Commands available via "pnpm run":
  dev
    next dev --turbo
  build
    next build
  build:worker
    cloudflare
  dev:worker
    wrangler dev --port 8772
  preview:worker
    pnpm build:worker && pnpm dev:worker

build-worker.tsから実装を読むのがよい。

opennextjsのnodejsランタイムサポート

Opennextjsの実装はvercel-cliを通さないので、nodejsランタイムを選択可能にする。 next-on-pagesでは、より制限されたedgeランタイムが強制されていた*2。 workerdはnode互換機能を提供しているので、next-on-pagesでビルドするより多くのNode.jsベースのツールやパッケージが使える。

developers.cloudflare.com

静的アセットのWorkersデプロイ

先のアップデートでは、画像などの静的アセットもWorkersにデプロイ可能となり、以前存在したWorkers Siteの機能が復活したように見受けられる。 next-on-pagesでは、静的なリソースはPagesに、動的な処理はその裏にいるWorkersに任せる構成が取られている。 だが、サーバーサイドで気の利いた処理を行いたい場合、すべてのFunctionsを忘れて、手前に_workers.jsのエントリーポイントを設置してリクエストを乗っ取らなければならなかった。 これは、Workers側の設定が透過的で、ログやメトリクスの収集や、複数のWorkersが連携するようなサービス指向の処理は実現は難しく、直接Workersを扱うよりも制約が多かった。 これらを踏まえると、コンピューティングのインターフェースをよりWorkers側に寄せるプロダクト戦略が進行していると考えられる。

developers.cloudflare.com

カスタムCacheHandlerの実装

Next.jsのカスタムCacheHandlerはすでにopennextjsで実装されている。 これもWorkers側にアセットの取り扱いが移行した影響によるものだろう。

opennext.js.org

この実装では、Next.jsのnext buildで生成された設定をさらに上書きし、グローバル関数にWorkers KVベースのCacheHandlerを追加する方法が取られている。 通常のNext.jsの設定経由ではこれを行わないのは、next buildの後に処理を追加する必要があるため、このような強引な方法にとっているのだろう。

移行のタイミング

とはいえ、新規プロジェクトでWorkersを使うユーザーにとって、Next.jsアプリビルドのアーキテクチャ変更は直接的な影響はない。 create-cloudflareの裏側のアーキテクチャが変わるだけだ。 Githubでのコントリビューターのコメントによると、「OpenNext.jsになるのは既定路線だが、next-on-pagesの方が現在運用されていて安定しているので、現時点では積極的に薦める段階ではない」という見解が示されている*3。 next-on-pagesからのマイグレーションはドキュメントがまだ整備されていないので、しばらく先だろう。

Cloudflare Workersの制限

注意すべき点は、無料プランにおけるCloudflare Workersの1MB(gzip)というアップロード制限は、多くのNext.jsアプリケーションにとってボトルネックとなり得ることだ。特に、opennextjsの実装ではEdge Runtimeの制約が取り払われ、Node.js向けのNext.js機能が使用可能になるため、設計上、バンドルサイズ(サーバーサイドのね)が大きくなることは自然だ。 SSRを活用して外部パッケージを導入する本格的なアプリケーションを作成する場合、すぐにこの上限に達することが想定される。 例えば、Prisma clientのように大規模なWebAssemblyバイナリを含むライブラリを導入すれば、1MBの制限を超える可能性はさらに高まる。

zenn.dev

実際、私がVercelが公開しているシンプルなShopifyストアフロントアプリをビルドしてみたところ、ファイルサイズが1.5MBに達していた。

# opennextjs-cloudflare/examples/vercel-commerce
❯ pnpm wrangler deploy --dry-run --minify --outdir out/

Total Upload: 5803.63 KiB / gzip: 1505.29 KiB
--dry-run: exiting now.

Cloudflare Workersの無料プランでNext.jsを使ったアプリケーションを展開する際は、この点を踏まえておく必要がある。

それでも、どうしてもゼロ円コースでWorkersを利用してReactなどのエコシステムにのってサービス開発して一発当てたいという苦学生は、後日私が提案する「SSR最低限React SPAツールキット」を参考にしてほしい。

シニアの皆さんは金を払え。