Invalid Date
Next.js + Prisma + Cloudflare Pages Edge Runtime: 3日間の戦いと学んだこと
はじめに
Next.js、Prisma、そしてCloudflare PagesのEdge Runtimeを組み合わせたアプリケーションのデプロイは、最新技術の恩恵を享受できる一方で、予期せぬ課題に直面することがあります。本記事では、私が直面した一連のビルドエラーと、それらをどのように解決していったか、そしてそこから得られた教訓を開発者ブログ記事としてまとめます。
終わりのないエラーとの戦い
私たちのNext.jsアプリケーションは、認証にnext-auth、データベースアクセスにPrismaを使用し、デプロイ先としてCloudflare PagesのEdge Runtimeを選択しました。しかし、デプロイの道のりは想像以上に険しいものでした。
1. Module not found: Can't resolve 'crypto' - Node.jsモジュールの壁
最初のビルドエラーは、next-authが依存するcryptoモジュールが見つからないというものでした。これは、Node.js環境に特化したモジュールがEdge Runtimeでは利用できないために発生します。
解決策: next.config.mjsにWebpackのポリフィル設定を追加し、crypto-browserifyなどのブラウザ互換パッケージをインストールすることで、この問題を回避しました。
2. Prisma AccelerateとDATABASE_URLの罠
cryptoエラーを乗り越えると、今度はPrismaに関するエラーに直面しました。InvalidDatasourceError: the URL must start with the protocol prisma://というエラーです。これは、PrismaのEdge Client (@prisma/client/edge) を使用する際に、Prisma Accelerateのprisma://プロトコルが必要であることを示しています。
prisma generateは成功するのに、npm run buildが失敗するという奇妙な現象が発生しました。調査の結果、ローカルのnext buildプロセスが.envファイルを正しく読み込んでいないことが判明しました。
解決策: ビルドコマンドを実行する際に、環境変数DATABASE_URLを明示的に渡すことで、この問題を回避しました。
例: $env:DATABASE_URL="<YOUR_PRISMA_URL>"; npm run build
3. next-authからAuth.js v5へのアップグレード
DATABASE_URLの問題を解決すると、今度はTypeError: Cannot read properties of undefined (reading 'slice')というエラーがnext-auth関連のルートで発生しました。これは、古いnext-authのPrismaアダプターが、Edge環境向けのPrisma Clientと互換性がないために発生するランタイムエラーでした。
解決策: next-authを最新のAuth.js v5 (next-auth@5) へアップグレードすることを決断しました。これには、パッケージのアンインストール/インストール、そしてsrc/auth.tsとsrc/app/api/auth/[...nextauth]/route.tsの大幅なリファクタリングが必要でした。
4. Cloudflare PagesによるEdge Runtimeの強制
Auth.js v5へのアップグレード後、ローカルでのビルドは成功するようになりましたが、Cloudflare Pagesでのデプロイ時にThe following routes were not configured to run with the Edge Runtimeというエラーが発生しました。これは、Cloudflare Pagesのビルドツールが、すべての動的ルートとページにexport const runtime = 'edge';の設定を強制しているためでした。
解決策: エラーメッセージにリストされたすべてのAPIルートとページ(src/app/[locale]/layout.tsxを含む)に、export const runtime = 'edge';を追加しました。
5. Edge Runtimeでのファイルシステムアクセス (/api/upload)
runtime = 'edge'を設定すると、今度は/api/uploadルートでModule not found: Can't resolve 'fs'というエラーが発生しました。Edge Runtimeはステートレスであり、ローカルファイルシステムへのアクセスができないため、fsやpathのようなNode.js固有のモジュールは使用できません。
解決策: 現状のファイルアップロード機能はEdge Runtimeでは動作しないため、デプロイを成功させるために、この機能を一時的に無効化しました。本格的な解決には、Cloudflare R2のようなクラウドストレージサービスを利用するよう、APIを書き換える必要があります。
6. i18n翻訳の課題
サイトがデプロイされ、ブラウザで表示されるようになったものの、翻訳キー(例: site_title)がそのまま表示される問題が発生しました。
原因1: HttpBackendの失敗: 最初の原因は、i18next-http-backendがEdge環境で翻訳JSONファイルをネットワーク経由で取得できないことでした。
解決策1: 翻訳データのバンドル: 翻訳JSONファイルを直接アプリケーションのバンドルに含めることで、ネットワークリクエストを不要にしました。これにより、src/app/i18n/settings.tsに翻訳データを集約し、src/app/[locale]/layout.tsxとsrc/app/[locale]/page.tsxがそこからデータを読み込むように変更しました。
原因2: Server/Client Component間のデータ受け渡し: generateMetadataでタブタイトルが正しく翻訳されるのに、ページ本体が翻訳されないという問題が発生しました。これは、サーバー側で初期化されたi18nextインスタンスが、クライアント側のコンポーネントに正しく渡されていなかったためです。React Server Componentsの制約により、関数を含むオブジェクトを直接クライアントに渡すことはできません。
解決策2: シリアライズ可能なデータの受け渡し: src/app/i18n/client.tsxを修正し、サーバーからクライアントへは、関数を含まないシリアライズ可能な翻訳データ(resources)のみを渡すようにしました。クライアント側は、そのデータを使って自身のi18nextインスタンスを初期化します。
原因3: lang_*キーのネイティブ表記: 最後に、言語セレクターの言語名が「英語」「日本語」のように翻訳されて表示されるべきところが、期待通りにネイティブ表記(「English」「日本語」)にならないという問題が発生しました。これは、common.jsonファイル内のlang_*キーの値が、ネイティブ表記ではなく、翻訳された言語名になっていたためです。
解決策3: common.jsonの修正: すべてのcommon.jsonファイル内のlang_*キーの値を、その言語のネイティブ表記に修正しました。
7. UI改善
最後に、ヘッダーのUI改善として、認証リンクと言語セレクターの間に区切り線を追加し、言語セレクターをドロップダウン形式に変更しました。また、地球儀アイコンの表示が崩れる問題も修正しました。
学んだこと
この3日間の戦いから、多くの重要な教訓を得ることができました。
- Edge Runtimeの複雑性: Edge Runtimeは高速ですが、Node.js環境とは異なる制約(ファイルシステムアクセス不可、特定のAPIの非互換性など)があります。既存のコードを移行する際には、これらの制約を深く理解する必要があります。
- ビルド環境の理解:
next buildが.envファイルを読み込まないなど、ビルドプロセスの挙動は環境によって異なります。デプロイ先のビルド環境の特性を把握することが重要です。 - Next.jsのServer/Client Componentの境界: Server ComponentとClient Component間のデータの受け渡しには厳格なルールがあります。特に、関数や非シリアライズ可能なオブジェクトを直接渡すことはできません。
- i18nの実装パターン: Edge環境やServer Componentを含むNext.jsアプリケーションでのi18n実装は、翻訳データの読み込み方法(ネットワークフェッチ vs. バンドル)や、Server/Client間でのインスタンスの共有方法を慎重に検討する必要があります。
- 反復的なデバッグの重要性: 一つのエラーを解決すると次のエラーが現れるという繰り返しの中で、忍耐強く、エラーメッセージを注意深く読み解き、一つずつ解決していくプロセスが不可欠です。
- ユーザーとのコミュニケーション: ユーザーからの具体的なフィードバック(特に「ネパール語版のタブだけネパール語だった」のようなヒント)は、問題解決の大きな助けとなります。
結論
Next.js、Prisma、Cloudflare Pages Edge Runtimeを組み合わせたアプリケーションのデプロイは、多くの課題を伴いましたが、最終的にはすべての問題を解決し、アプリケーションを正常に動作させることができました。この経験は、最新のWeb開発における複雑性と、それを乗り越えるための深い理解と粘り強さの重要性を改めて教えてくれました。