🤖

OpenNextでCloudflare Workersの複数環境を使う

に公開

OpenNextはVercel以外の環境(特にCloudflare Workers)でNext.jsを動作させるための便利なプロジェクトですが、それぞれの制約をうまく糊付けするために意外とはまりどころが多かったりします。

今回は素直なOpenNext x Cloudflare Workersプロジェクトを複数環境対応させるために必要なことと、その際のはまりどころをまとめます。

はじめに

Cloudflare Workersで複数環境を運用するには以下のドキュメントにあるようにwrangler.jsoncに複数環境の設定をする方法と、Cloudflare WorkersのPreview URLsを使った方法があります。

https://developers.cloudflare.com/workers/wrangler/environments/

https://developers.cloudflare.com/workers/configuration/previews/

Preview URLsはWorkerを複数用意する必要がなく、管理画面で有効化すれば簡単に使えるようになるので手軽ですが、固定のドメインを割り当てる方法が公式でサポートされていないので、検証環境としてstaging.example.comのようなドメインで運用するためには作られていないようです。

それに対してwranglerで環境切り分けをする方法は、Workerを複数用意する必要はあるものの、ドメインの割り当てや各種サービスとのBindingも本番環境と全く同じようにできるので、検証環境を使うのにはこちらを使う方が良さそうです。

使い分け方としては、本番環境や検証環境といった従来通りの複数環境運用にはwrangler.jsoncでの切り替えを使い、Pull Requestごとのプレビュー環境やリモートでの動作確認にはPreview URLsを使うのがいいと思います。

今回はwrangler.jsoncを使ったプロジェクトの切り換え方についてまとめてます。

プロジェクトの初期化

OpenNextのドキュメント通りにプロジェクトを初期化します。

https://opennext.js.org/cloudflare/get-started

npm create cloudflare@latest -- my-next-app --framework=next --platform=workers

wrangler.jsoncに複数環境を設定

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データベースを環境ごとに割り当てています。

デプロイコマンドの修正

このままではプロジェクトに最初から設定してあるデプロイコマンドが動かないので、一部修正する必要があります。

package.json
{
    ...
    "deploy:production": "opennextjs-cloudflare build && opennextjs-cloudflare deploy --env=production",
    "deploy:staging": "opennextjs-cloudflare build && opennextjs-cloudflare deploy --env=staging",
    ...
}

opennextjs-cloudflare deployコマンドに--envパラメーターを渡すことでデプロイする環境を指定することができます。

package.json
    "upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload --env=staging",
    "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview --env=staging",

upload``previewコマンドも同様にどの環境で動かすか指定します。

package.json
    "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という関数を提供しています。

next.config.ts
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に追加した場合は、開発時にどの環境をエミュレートするかを指定する必要があります。

next.config.ts
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を書くことができます。

wrangler.jsonc
{
  "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.jsoncnodejs_compatフラグを有効化することで、process.env経由でアクセスできるようになります。

現在の環境変数運用のベストプラクティス

https://opennext.js.org/cloudflare/howtos/env-vars

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