OpenNextでCloudflare Workersの複数環境を使う
OpenNextはVercel以外の環境(特にCloudflare Workers)でNext.jsを動作させるための便利なプロジェクトですが、それぞれの制約をうまく糊付けするために意外とはまりどころが多かったりします。
今回は素直なOpenNext x Cloudflare Workersプロジェクトを複数環境対応させるために必要なことと、その際のはまりどころをまとめます。
はじめに
Cloudflare Workersで複数環境を運用するには以下のドキュメントにあるようにwrangler.jsonc
に複数環境の設定をする方法と、Cloudflare WorkersのPreview URLsを使った方法があります。
Preview URLsはWorkerを複数用意する必要がなく、管理画面で有効化すれば簡単に使えるようになるので手軽ですが、固定のドメインを割り当てる方法が公式でサポートされていないので、検証環境としてstaging.example.com
のようなドメインで運用するためには作られていないようです。
それに対してwranglerで環境切り分けをする方法は、Workerを複数用意する必要はあるものの、ドメインの割り当てや各種サービスとのBindingも本番環境と全く同じようにできるので、検証環境を使うのにはこちらを使う方が良さそうです。
使い分け方としては、本番環境や検証環境といった従来通りの複数環境運用にはwrangler.jsonc
での切り替えを使い、Pull Requestごとのプレビュー環境やリモートでの動作確認にはPreview URLsを使うのがいいと思います。
今回はwrangler.jsonc
を使ったプロジェクトの切り換え方についてまとめてます。
プロジェクトの初期化
OpenNextのドキュメント通りにプロジェクトを初期化します。
npm create cloudflare@latest -- my-next-app --framework=next --platform=workers
wrangler.jsonc
に複数環境を設定
/**
* For more details on how to configure Wrangler, refer to:
* https://developers.cloudflare.com/workers/wrangler/configuration/
*/
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-next-app",
"main": ".open-next/worker.js",
"compatibility_date": "2025-03-01",
"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
"assets": {
"binding": "ASSETS",
"directory": ".open-next/assets"
},
"observability": {
"enabled": true
},
"env": {
"production": {
"routes": [{ "pattern": "example.com", "custom_domain": true }],
"d1_databases": [
{
"binding": "DB",
"database_name": "test",
"database_id": "xxxxx"
}
],
"preview_urls": false
},
"staging": {
"routes": [
{ "pattern": "staging.example.com", "custom_domain": true }
],
"d1_databases": [
{
"binding": "DB",
"database_name": "test",
"database_id": "xxxxx"
}
],
"preview_urls": true
}
},
"placement": { "mode": "smart" }
}
"env"
部分が環境設定をしているところです、wrangler.jsonc
に書けることはなんでも環境ごとにenvの中に書くことができます。今回はカスタムドメインの割り当てとD1データベースを環境ごとに割り当てています。
デプロイコマンドの修正
このままではプロジェクトに最初から設定してあるデプロイコマンドが動かないので、一部修正する必要があります。
{
...
"deploy:production": "opennextjs-cloudflare build && opennextjs-cloudflare deploy --env=production",
"deploy:staging": "opennextjs-cloudflare build && opennextjs-cloudflare deploy --env=staging",
...
}
opennextjs-cloudflare deploy
コマンドに--env
パラメーターを渡すことでデプロイする環境を指定することができます。
"upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload --env=staging",
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview --env=staging",
upload``preview
コマンドも同様にどの環境で動かすか指定します。
"cf-typegen": "wrangler types --env-interface CloudflareEnv ./cloudflare-env.d.ts --env staging",
cf-typegen
コマンドはちょっと初見で何のためのコマンドか分かりづらいですが、wrangler.jsonc
を読んで、bindingや環境変数などの設定からTypescript向けの型定義を生成するためのコマンドです。このコマンドのおかげでD1データベースなどのインスタンスに型安全にアクセスできます。
どの環境から型情報を生成するかを指定しましょう。ほとんどの場合どの環境でも使いたいbindingsや環境変数は変わらないのでどれか一つ指定しておけば問題ないです。
next.config.js
を編集する
ここまででCloudflare Workersとwranglerはうまく動きますが、ローカルでの開発環境がまだ動かないと思います。
なぜならnext dev
コマンドで動作するローカル開発環境には、Cloudflare Workersの環境もD1データベースなどのBindingsも存在しないからです。
OpenNextはこれを解決するためのinitOpenNextCloudflareForDev
という関数を提供しています。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;
// added by create cloudflare to enable calling `getCloudflareContext()` in `next dev`
import { initOpenNextCloudflareForDev } from '@opennextjs/cloudflare';
initOpenNextCloudflareForDev();
プロジェクトを作成した時点でこのようにnext.config.ts
の中でinitOpenNextCloudflareForDev
が呼び出されているので普通に使う分には問題ないですが、今回のように複数環境をwrangler.jsoncに追加した場合は、開発時にどの環境をエミュレートするかを指定する必要があります。
initOpenNextCloudflareForDev({
environment: "staging",
});
これでローカル開発でもD1データベース等にアクセスできるようになるはずです。
環境変数について
今回の趣旨とはちょっと外れますが、いつかの自分のために環境変数の取り扱いもまとめておきます。OpenNextとCloudflare Workersの組み合わせにおける環境変数は、動作環境や環境変数宣言の方法の多様さからめちゃくちゃ複雑で分かりづらいです。
まず、OpenNextで開発したプロジェクトが環境変数にアクセスする環境には以下の4つがあります。
ローカル開発環境(next dev)
next dev
コマンドで動作する基本的な開発環境です。
ローカル開発環境(wrangler dev)
wranglerでCloudflare Workersをローカルでエミュレートした環境です。OpenNextではopennextjs-cloudflare preview
コマンドを使うことで立ち上げられます。
ビルド環境
Next.jsのアプリケーションをビルドするための環境です。Next.js
はSSGのためにビルド時にもAPI通信を行うことがあるので、ここでも環境変数へのアクセスが必要になることがあります。またNEXT_PUBLIC_
からはじまる環境変数はブラウザからでもアクセスできるようにビルド時にインライン化されるので、ビルド環境からアクセスできる必要があります。
Cloudflare Workers上
検証環境や本番環境など実際にCloudflare上で動作する環境です。
対して環境変数の設定方法には以下の6つがあります。
OSの環境変数
普通にOSの環境変数に設定する方法です。ローカル環境でもサーバーでも使えますが環境依存が強いです。
process.env.XXXX
でアクセスします。
.env
ファイル
.env
ファイルをプロジェクトのルートに置く方法です。Next.jsはこのファイルをサポートしているのでNext.jsのビルド時やnext dev
によって立ち上げられた環境ではアクセスできますが、wranglerはこれをサポートしていないのでopennextjs-cloudflare preview
コマンドで立ち上げられた環境からはアクセスできません。また、.env
ファイルはGitで管理しないのが定石となっているので、Cloudflare WorkersやCIなどの環境からアクセスすることができません。
process.env.XXXX
でアクセスします。
.dev.vars
ファイル
wranglerがサポートする環境変数ファイル名です。使い方は.env
ファイルと同じでopennextjs-cloudflare preview
コマンドで立ち上げた環境からアクセスできますが、next dev
で立ち上げたローカル環境からはアクセスできません。
ここで定義されている環境変数はprocess.env.
ではなく以下のようにCloudflare Contextからアクセスします。
const { env } = getCloudflareContext()
env.XXX
wrangler.jsonc
wranglerの設定ファイルにもenvを書くことができます。
{
"name": "my-worker-dev",
"vars": {
"API_HOST": "example.com",
"API_ACCOUNT_ID": "example_user",
"SERVICE_X_DATA": {
"URL": "service-x-api.dev.example",
"MY_ID": 123
}
}
}
コードからのアクセス方法は.dev.vars
に書くのと変わりませんが、wrangler.jsonc
に書いた環境変数はデプロイ時にCloudflare Workers上に保存されるので、Workersからも使えるようになります。
Cloudflare Workersのコンソールから設定
前述したようにwrangler.jsonc
に環境変数を定義してデプロイしなくても管理画面上から設定することもできます。コードからのアクセス方法もgetCloudflareContext
経由になりますが、環境変数のタイプをText
ではなくSecrets
(秘匿情報)にするとprocess.env
経由でアクセスしなくてはいけなくなります。めちゃくちゃややこしいですね。
また後者3つのgetCloudflareContext
経由でアクセスする環境変数に関してはwrangler.jsonc
でnodejs_compat
フラグを有効化することで、process.env
経由でアクセスできるようになります。
現在の環境変数運用のベストプラクティス
OpenNextのドキュメントに従うのが正解だと思います。ローカル環境では.env
ファイルを使って開発をし、Cloudflare Workers上で動かすときにはCloudflare Workersの管理コンソールに手動で環境変数を追加しましょう。.dev.vars
にはNEXTJS_ENV
だけを指定し、wrangler.jsonc
ファイル内のenv
は空にしておいていいです。
環境変数へのアクセスはprocess.env
を使った方法に制限することで、環境ごとにアクセス方法が変わってしまうのを避けることができます。
トラブルシューティング
デプロイすると環境変数が消える
opennextjs-cloudflare deploy
コマンドはデフォルトで管理コンソール上で設定されたSecret
以外の環境変数をwrangler.jsonc
ファイル内のenv
で上書きします。これはenv
フィールドをwrangler.jsonc
に書いていなくても、空のenv
として上書きをしてしまいます。
この挙動を抑制したい場合は--keep-vars
オプションをつけましょう。
opennextjs-cloudflare deploy -- --keep-vars
Discussion