【Node.js/Next.js】環境変数(.env)のチートシート|2022年1月版
概要
本記事では、Next.jsにおける環境変数について解説しています。環境変数の基礎だけでなく、本番環境とステージング環境で異なる環境変数を読み込ませたい場合の対処方法も掲載していますので、ぜひ参考にしてもらえればと思います。(需要があるかは諸説)
Next.jsの環境変数周りは少し複雑で公式ドキュメントを読んでも理解しきれないこともあると思います。この記事ではできる限り、初見さんでも理解できるぐらいに噛み砕いて書いていこうと思います。
本題の前に
前回の記事が初投稿でしたが、予想より多くのいいねをいただき、驚きを隠せてないです。(本当にありがとうございます🙏)
今後も定期的に投稿できるように頑張っていこうと思っています。しばらくはNext.jsの記事が多いと思いますが、その後はNest.jsやFastAPIの記事を書いていければと考えています。
Next.jsにおける環境変数
- Next.jsの環境変数はNode.jsと同様、
process.env
に格納される - Next.jsをビルドした段階で格納される
- 格納する値は
.env*
ファイルに定義できる(後述参照) - 通常はクライアントサイドでは読み込めない※1
環境変数の読み込み
バージョン毎の挙動差分
Next.jsの環境変数は周りは、バージョンによって挙動が異なります。旧バージョンを利用しても問題ありませんが、利便性やセキュリティ性が向上するため、筆者的にはアップデート推奨します。
v9.4以上(推奨)
前述しましたが通常、クライアントサイドでは、環境変数を読み込むことができません。と言っても文字だけでは分かりづらいと思いますので、実際のファイルで見てみようと思います。
HOGE=FOO
// サーバーサイド
console.log(process.env.HOGE)
=> FOO
// クライアントサイド
console.log(process.env.HOGE)
=> undefined
特に設定しないと上記のようにクライアントサイドでの読み込み結果がundefined
となってしまいます。クライアントサイドで環境変数を使用したい場合、.env*
で定義する環境変数にプレフィックスを付ける必要があります。
NEXT_PUBLIC_
をつける
クライアントサイドで使用する場合は変数名にクライアントサイドで環境変数を使用したい場合、.env*
で定義する際に環境変数名をNEXT_PUBLIC_
から始まる形式にします。これにより、特に設定せずに環境変数をサーバーサイド/クライアントサイドの両方で読み込むことが可能になります。
HOGE=FOO
NEXT_PUBLIC_HOGE=NEXT_PUBLIC_FOO
// サーバーサイド
console.log(process.env.HOGE)
console.log(process.env.NEXT_PUBLIC_HOGE)
=> FOO
=> NEXT_PUBLIC_FOO
// クライアントサイド
console.log(process.env.HOGE)
console.log(process.env.NEXT_PUBLIC_HOGE)
=> undefined
=> NEXT_PUBLIC_FOO
v9.4以前(非推奨)
v9.4以前の場合、上記の方法は使用することができません。クライアントサイドで使用したい場合は、以下の方法で環境変数を設定ファイルに定義する必要があります。
next.config.js
で設定する
クライアントサイドで使用する場合はNext.jsの設定ファイルであるnext.config.js
内で定義することでクライアントサイドに環境変数を設定できます。この方法はv9.4以上でも利用可能ですが、.env*
で定義したものをnext.config.js
側にも定義しないといけないため、運用がやや面倒です。個人的には運用しやすいプレフィックスする方法がオススメです。
HOGE=FOO
module.exports = {
env: {
HOGE: process.env.HOGE,
},
}
// サーバーサイド
console.log(process.env.HOGE)
=> FOO
// クライアントサイド
console.log(process.env.HOGE)
=> FOO
環境変数ファイルの種類
Next.jsにおける環境変数ファイルは以下の通りです。種類が多いことに加え、読み込まれる環境と優先度があるため、かなり複雑になっています。筆者はどのようにすれば、万人が理解できる構成にできるか頭を悩ませました。後述で筆者が、実際に社内で採用した構成を紹介したいと思います。
ファイル | 用途 | 適応環境 | プライオリティ | 利用頻度 |
---|---|---|---|---|
.env | 全ての環境で読み込まれる。環境に依存しない共通の変数を定義する際に使用。 | 全環境 | 4 | 高 |
.env.development | 開発環境で読み込まれる。開発環境で変数を定義する際に使用。 | 開発環境(yarn dev 実行時) |
3 | 高 |
.env.production | 本番環境で読み込まれる。本番環境で変数を定義する際に使用。 | 本番環境(yarn start ,yarn build 実行時) |
3 | 高 |
.env.local | 全ての環境で読み込まれる。環境に依存しない共通のAPIキーやDBパスワードなど機密情報を定義する際に使用。 | 全環境 | 2 | 低 |
.env.development.local | 開発環境で読み込まれる。開発環境におけるAPIキーやDBパスワードなど機密情報を定義する際に使用。 | 開発環境(yarn dev 実行時) |
1 | 中 |
.env.production.local | 本番環境で読み込まれる。本番環境におけるAPIキーやDBパスワードなど機密情報を定義する際に使用。 | 本番環境(yarn start ,yarn build 実行時) |
1 | 中 |
package.json
"scripts": {
"dev": "next",
"start:next": "next start",
"build": "rm -rf .next && next build"
}
読み込まれる環境は全環境
、開発環境
、本番環境
の3種類あり、プライオリティが高いものから順に読み込まれていきます。少し分かりづらいと思うので、図で可視化させてみます。
ビルドを実行するとプライオリティ順に基づいて、環境変数ファイルを読み込んでいきます。途中、同名の変数があった場合はオーバーライトされます。上記の開発環境側の場合、.env
で定義した変数HOGEは、プライオリティの高い.env.development.local
で定義した値にオーバーライトされるため、HOGE_ENV_DEVELOPMENT_LOCAL
となります。
個人的なベストプラクティス
自身の携わる開発では、上記全て使うと管理ができずに破綻する未来が見えていたので、もっとシンプルな仕組みにしました。環境変数は、基本的にGithubに載せないのが前提(環境変数自体が機密情報であることが多い)という観点から.env.*.local
を廃止しました。さらに共通変数という概念が混乱を招くと考え.env
も併せて廃止し、以下のような構成になりました。
ファイル | 用途 |
---|---|
.env.development | 開発環境でする環境変数 |
.env.production | 本番環境でする環境変数 |
非常にシンプルな作りになったと思います。しかし、この構成にしたことで機密情報も同じファイルに定義する形になってしまい、Github上で管理ができない状態になってしまいました。そこで、Cloud Key Management Service(KMS)で暗号化して暗号化したファイルをGithubで管理する方式を採用しました。こちらについては後日、別記事でまとめたいと思います。
追記(2021/06/09)
上記のベストプラクティスですが、知り合いのCさんから「開発する際にローカル環境に依存するケースが出てくるから、それにも対応できたほうが良い」と指摘もらったので、その課題を改善する方法を少し考えてみました。
環境で依存するケースに対応する場合に適しているファイルはどれか考えた時、.env.production.local
か.env.development.local
のいずれかが最適と思いました。理由としては、.env
と.env.local
は全ての環境で読み込まれてしまうという仕様上、前述2つと役割が重複してしまい、結果として管理の手間が増えてしまうと考えたからです。
環境毎の設定方法
前述したファイルの種類を見て察した人もいると思いますが、本番環境における環境変数は.env.production
と.env.production.local
の2種類しか存在していません。WEBサービス構築時、本番環境(実際にサーバー上で起動のこと)はプロダクション環境とステージング環境を用意することが多いと思います。するとプロダクション環境とステージング環境で別々の環境変数を定義したい場合、どうするべきかという課題が発生します。
解説する方法の前提
今回は以下の環境での設定方法になります。筆者が基本的にGCPを使用しているため、GCP基準でなってしまっていますが、競合サービスであるAWSのEC2とかでも問題ないと思います。
- Cloud Run
- Google App Engine(GAE)
シェルスクリプトでファイルを構築する
ビルド前に別に用意した環境変数ファイルから.env.production
(または.env.production.local
)に内容をコピーする方法です。具体的なコードは以下を参考にしてもらえればと思います。
#!/bin/bash
cd $(dirname $0)
APP_ENV=$1
cp .env.$APP_ENV.base .env.production
"scripts": {
"dev": "next",
"start:next": "next start",
"build": "rm -rf .next && next build",
"build:env": "./env.sh"
}
HOGE=FOO
$ yarn build:env production
$ yarn build
$ yarn start
.env.production.base
というベースファイルを用意してスクリプトで.env.production
にコピーする簡単なロジックになります。ビルド前に実行することで、ビルド時に読み込まれる値をベースファイルの中身にできます。ステージング環境の場合は.env.staging.base
とかを用意して、yarn build:env staging
を実行してからビルドすれば問題ありません。
これはあくまで方法の1つで、KMSやSecret Managerなどの秘匿情報を管理するサービスを利用する方法もあると思います。別にもっといい方法があれば、ぜひコメントしてもらえると幸いです!
まとめ
- Next.jsのバージョンで挙動が異なる
- 特に問題なければv9.4以上にアップデートする
- クライアントサイドで環境変数を使用する場合は、
NEXT_PUBLIC_
を付ける - Githubにはできる限り、Pushしない
-
.env*
には適応される環境と優先度がある - 環境毎に分ける場合はシェルスクリプト等で制御が必要
今回は、Next.jsにおける環境変数についてまとめました。できるだけ噛み砕いてまとめたつもりですが、分かりづらい箇所があるかもしれません。(後日、自社のエンジニアに読んでもらってリライトしようと思います)少しでも参考になったと思った方は、いいねしていただけると記事執筆の励みになりますので、よろしくお願いします!
次回は環境変数をCloud Key Management Service(KMS)で暗号化/復号化する方法をまとめる予定です💪
関連記事
参考記事
Discussion