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.tssrc/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はステートレスであり、ローカルファイルシステムへのアクセスができないため、fspathのような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.tsxsrc/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日間の戦いから、多くの重要な教訓を得ることができました。

結論

Next.js、Prisma、Cloudflare Pages Edge Runtimeを組み合わせたアプリケーションのデプロイは、多くの課題を伴いましたが、最終的にはすべての問題を解決し、アプリケーションを正常に動作させることができました。この経験は、最新のWeb開発における複雑性と、それを乗り越えるための深い理解と粘り強さの重要性を改めて教えてくれました。


← Previous Entry:
← Back to Blog List