process.env.NODE_ENV ってなに？

![Your paragraph text.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254825/363463f2-9796-f52e-b33d-33053d97ebbe.jpeg)

## 1. NODE_ENV ってなに？

NODE_ENV は Node.js でよく使う[環境変数](https://wa3.i-3-i.info/word11027.html)です。

### <font color="#5f9ea0">◯ 環境変数 NODE_ENV に指定される値</font>

以下２つが指定されます。



|項目|内容|
|:-:|:-:|
|production|本番環境のサーバーの NODE_ENV に設定する|
|development|開発環境のパソコンの NODE_ENV に設定する|

<details><summary>補足</summary>

`production` は開発環境のパソコンにも動作確認のため設定します。上記の図や表では簡単のため省略しました。

</details>



### <font color="#5f9ea0">◯ production と development の違い</font>

公式ドキュメントの和訳です。

> `NODE_ENV` を `production` に設定することは一般的に、以下のことを保証します
> - ログ採取は最小限で必要不可欠なレベルに限定されます
> - より高いレベルのキャッシュが導入され、パフォーマンスの最適化が図られます

* [production と development の違い - Node.js](https://nodejs.org/en/learn/getting-started/nodejs-the-difference-between-development-and-production)


<!--

> Setting the environment to production generally ensures that
> - logging is kept to a minimum, essential level
> - more caching levels take place to optimize performance


* [What is NODE_ENV in Express? - Stackoverflow](https://stackoverflow.com/questions/16978256/what-is-node-env-in-express/16979503)

-->


### <font color="#5f9ea0">◯ サンプルコード</font>

```bash:macOS, Ubuntu - bash
#
# 1. 環境変数を設定
#
export NODE_ENV="production"


#
# 2. Node.js 起動
#
node
```

```js:Node.js
process.env.NODE_ENV
// 'production'
```

<details><summary>(1) Windows - コマンドプロンプト</summary>


```
# 設定
set NODE_ENV=production

# 表示
echo %NODE_ENV%

# 一覧の表示
set
```

</details>

<details><summary>(2) Windows - PowerShell</summary>


```
# 設定
$env:NODE_ENV = "production"

# 表示
echo $Env:NODE_ENV

# 一覧の表示
Get-ChildItem Env:
```

</details>

<details><summary>(3) macOS, Ubuntu - bash, zsh</summary>

```
# 設定
export NODE_ENV="production"

# 表示
echo ${NODE_ENV}

# 一覧の表示
env
```

</details>










### <font color="#5f9ea0">◯ 問題</font>

`NODE_ENV` に指定される値として一般的でないものは、つぎのうちどれですか？

1. development
2. staging
3. production

<details><summary>解答</summary>

2\. staging

* [ステージング環境](https://wa3.i-3-i.info/word16417.html) -「分かりそう」で「分からない」でも「分かった」気になれる...
* [NODE_ENV に development と production 以外を入れると辛い](https://blog.p1ass.com/posts/node-env/)

上記リンクを補足します。なぜ「NODE_ENV に development と production 以外を入れると辛」くなるのでしょうか？

自分が作っているアプリは `NODE_ENV` を参照します。一方で、例えば Next.js や Nuxt と言ったフレームワークも `NODE_ENV` を参照します。

フレームワークは `NODE_ENV` には `development` または `production` が代入されていることを前提に動作しています。もし勝手に `NODE_ENV` に例えば `staging` を設定してしまった場合、フレームワークが動作しなくなります。

もし `staging` を定義したい場合は、別途自分のアプリ専用の環境変数、例えば `APP_ENV` のような環境変数を定義すればよいのかなと思いました（小並感）。

| 環境 | NODE_ENV | APP_ENV |
|:--:|:-:|:-:|
|開発|development|development|
|ステージング|production|staging|
|本番|production|production|

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">NODE_ENV=productionが最適化のために使われてるので、NODE_ENV=stagingのような使い方はしないで別の名前の環境変数を使おうという話。 &quot;NODE ENV=production is a lie - YouTube&quot; <a href="https://t.co/7uCLayNlTy">https://t.co/7uCLayNlTy</a> <a href="https://twitter.com/hashtag/nodejs?src=hash&amp;ref_src=twsrc%5Etfw">#nodejs</a> <a href="https://twitter.com/hashtag/video?src=hash&amp;ref_src=twsrc%5Etfw">#video</a></p>&mdash; azu (@azu_re) <a href="https://twitter.com/azu_re/status/1725493560106205463?ref_src=twsrc%5Etfw">November 17, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

</details>








## 2. process.env ってなに？

1. `process.env` プロパティ
2. `process` 変数
3. `globalThis` 変数


### <font color="#5f9ea0">(1) process.env プロパティ</font>

process.env プロパティは、[環境変数](https://wa3.i-3-i.info/word11027.html)を保存しています。

> process.env プロパティは、ユーザーの環境を含むオブジェクトを返します。
> The process.env property returns an object containing the user environment.

* [process.env - Node.js](https://nodejs.org/api/process.html#process_process_env)




### <font color="#5f9ea0">(2) process 変数</font>

> process オブジェクトは、現在の Node.js **プロセス** に関する情報や制御を提供します。
> The process object provides information about, and control over, the current Node.js process.

* [Process - Node.js](https://nodejs.org/api/process.html#process_process)
* [プロセス - 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word11032.html)









### <font color="#5f9ea0">(3) globalThis 変数</font>

`globalThis` からも環境変数が参照できます。

```:bash
export NODE_ENV='development'
node
```
```js:Node.js
process.env.NODE_ENV
// 'production'

globalThis.process.env.NODE_ENV
// 'production'
```

<details><summary>globalThis</summary>

`globalThis` からグローバルスコープの変数が参照できます。

```:bash
node
```
```js:Node.js
// (1) var で宣言した変数はグローバルスコープに登録される
var hello = "hello"
globalThis.hello
// 'hello'

// (2) const で宣言した変数はローカルスコープに登録される
const nihao = "nihao"
globalThis.nihao
// undefined
```

> globalThis のプロパティにはグローバルスコープの this の値が含まれており、通常はグローバルオブジェクトに類似しています。
> The globalThis global property contains the [global this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#global_context) value, which is usually akin to the [global object](https://developer.mozilla.org/en-US/docs/Glossary/Global_object).

* [globalThis - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis)

</details>


<details><summary>グローバルスコープ</summary>

:::note warn

「7. 型定義」で後述する TypeScript で `process.env.NODE_ENV` に型をつけたいときに頭の片隅にあるとよい知識かなと思いました。

:::


この JavaScript のグローバルスコープと Python のグローバルスコープで意味が異なるので個人的に混乱しました。JavaScript のグローバルスコープは以下のとおりです。

❌ そのモジュールの内側ならどこからでも参照できるスコープ
⭕ すべてのモジュールからどこからでも参照できるスコープ

> [グローバルスコープ- MDN Web Docs 用語集](https://developer.mozilla.org/ja/docs/Glossary/Global_scope)
> プログラミング環境において、グローバルスコープとは、他のすべてのスコープを含み、他のすべてのスコープからアクセス可能なスコープを指します。

> [ローカルスコープ - MDN Web Docs 用語集](https://developer.mozilla.org/ja/docs/Glossary/Local_scope)
> ローカルスコープは 変数 をローカルにする変数の特性です（つまり、変数名は グローバルスコープ ではないスコープ内の 値 にのみ結び付けられます）。

</details>


<details><summary>globalThis と global と this</summary>

```js:Node.js
var message = "Hello, world!"

globalThis.message
// "Hello, world!"

global.message
// "Hello, world!"

this.message
// "Hello, world!"
```

昔は `globalThis` ではなく `global` を使っていたそうです。

> 安定性: 3 - レガシー。代わりに globalThis を使用してください。
> Stability: 3 - Legacy. Use globalThis instead.

* [global - Node.js](https://nodejs.org/docs/v20.18.0/api/globals.html#global)

<br />

詳細はこちら↓

* [👻globalThis👻と🌏global🌏と🌝this🌝 - Qiita](https://bit.ly/3UgeWff)

</details>








### <font color="#5f9ea0">◯ 問題</font>

`NODE_ENV` を参照するとエラーになってしまうものは、つぎのうちどれですか？


```bash:bash
# 環境変数を前置してコマンドを実行することもできます。
# この書き方は `package.json` の `script` プロパティで見ることがあります。
NODE_ENV="production" node
```
```js:Node.js
// (1)
env.NODE_ENV

// (2)
process.env.NODE_ENV

// (3)
globalThis.process.env.NODE_ENV
```


<details><summary>解答</summary>

(1) `env.NODE_ENV`

</details>







## 3. 環境変数はいつ使うの？

開発環境のパソコンと本番環境のサーバーで異なる値を使いたいときに使うのかなと思っています。

* [環境変数](https://wa3.i-3-i.info/word11027.html) -「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

### <font color="#5f9ea0">(1) 開発環境と本番環境を識別するとき</font>

<details><summary>詳細</summary>

* 開発環境のコードを本番環境にデプロイするとデバッグログがブラウザに表示されてセキュリティがよろしくない状態になることがあります。デプロイの切り替えを手動で行うとミスしてしまう可能性があります。このような事態を避けるために開発環境には `development`, 本番環境には `production` を指定して自動で環境を区別するとよいような気がします
* Python のフレームワークである Flask の事例ですが、開発環境のコードをデプロイしてしまい、デバッグ用の SQL を生でブラウザから実行できる画面が表示された状態になり大変なことになったという話を YouTube で聞いたことがあります。リンク先の YouTube の動画で Flask の作者 Armin Ronacher が 14:49 に incident 事件について語ってくれています
* [Armin Ronacher, "Flask for Fun and Profit", PyBay2016](https://youtu.be/1ByQhAM5c1I?si=2puxLmhYSoehQu4d&t=889)

</details>



### <font color="#5f9ea0">(2) パスワードを保存するとき</font>

<details><summary>詳細</summary>


* パスワードをソースコードに直接書き込み、GitHub に公開してしまい大変なことになったという話もたまに聞きます。
* ソースコードを公開しなければパスワードをベタ書きしてもいいんじゃない？って思ったのですが、公開、非公開の有無に関わらずパスワードは環境変数に書きましょうね、というのが一般的なお作法のようです。
* 考えてみるとソースコードを GitHub で公開しなかったとしてもソースコードにパスワードを書いてしまった場合、git clone するごとにパスワードがいろいろなところに拡散してしまうことになります。環境変数を使い一箇所だけでパスワードを保存しておくほうが安全な気がします（小並感）。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">GitHubで「OPENAI_API_KEY」などのワードで検索すると、多くの開発者が秘密鍵を公開リポジトリに誤ってアップロードしている例が見つかります。これは企業に対する教訓となります。たとえ開発者のような技術者であっても、信用してはいけません。GitHubとOpenAIは実際に、公開GitHubリポジトリにアップ… <a href="https://t.co/KPelm617eb">pic.twitter.com/KPelm617eb</a></p>&mdash; Jeffry Alvarado (@jalva_dev) <a href="https://twitter.com/jalva_dev/status/1802200451708051820?ref_src=twsrc%5Etfw">June 16, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

</details>







## 4. 環境変数とシェル変数



||環境変数とシェル変数の違い|
|:-|:-:|
|Windows - コマンドプロンプト|なし *1|
|Windows - PowerShell|あり|
|macOS, Ubuntu - bash, zsh|あり|

\*1 Windows - コマンドプロンプトではすべて環境変数として扱われます。



例えば `export` コマンドは環境変数を設定するためのものです。`export` をつけ忘れると **シェル変数** だけしか変更されず、**環境変数** は変更されません。

```:bash
#
# 1. export をつける
#
export NODE_ENV='development'
echo ${NODE_ENV}
# 'development'

#
# 2. export をつけない
#
APP_ENV='production'  # <--- export をつけなくても
echo ${APP_ENV}
# 'production'          <--- bash では表示されるが...

node
```
```js:Node.js
process.env.NODE_ENV
// 'development'

process.env.APP_ENV
// ???
```


### <font color="#5f9ea0">◯ 問題</font>

上記 `???` には何が表示されますか？

1. `production`
2. `undefined`

<details><summary>答え</summary>

2\. `undefined`


* [process.env.NODE_ENV is undefined - Stackoverflow](https://stackoverflow.com/questions/11104028/process-env-node-env-is-undefined)

</details>






## 5. 環境変数とファイル

:::note warn

環境変数に書き込むのとファイルに書き込むことの違いってなんなのでしょうか？なんで環境編巣が存在するのでしょうか？ファイルから値を読み込むのは面倒だから、簡単に環境変数からよく使う値だけを値を読み込めるようにしましたみたいな感じなんでしょうか？🤔

:::




<details><summary>個人的な備忘録です。</summary>


そもそも環境変数とは？

> 簡単に言うと環境変数とは、プログラムの挙動を調整するためのパラメータの一種 ... 中略 ... OSとしてプログラム実行時に指定するパラメータは ... 中略 ... 2種類しかありません。その1つがコマンドライン引数、もう一つが環境変数です。
* [環境変数ってなに? ( Linux ) - Qiita](https://bit.ly/3QXmz8J)

### <font color="#5f9ea0">(1) コマンドから呼び出すとき</font>

環境変数に書き込むとコマンドの引数に与えるのが簡単

```bash:bash
# いいサンプルが思い浮かばない... orz
export DB_PASS=password
echo ${DB_PASS}
```

ファイルに書き込むとコマンドの引数に与えるのが面倒（頑張ればできる）

```bash:bash
echo "password" > DB_PASS.txt
echo $(cat DB_PASS.txt)
```


### <font color="#5f9ea0">(2) Node.js から呼び出すとき</font>

環境変数に書き込むと読みこむのが簡単

```bash:bash
export DB_PASS=password
node
```
```js:Node.js
console.log(process.env.DB_PASS)
```

ファイルに書き込むと読みこむのが面倒（頑張ればできる）

```bash:bash
echo '{ "DB_PASS": "password" }' > ./env.json
node
```
```js:Node.js
const fs = require('fs')
const env = JSON.parse(fs.readFileSync('./env.json', 'utf8'))
console.log(env.DB_PASS) 
```

</details>














## 6. 型定義

:::note warn

あんまりよくわかっていません...

:::


TypeScript をお使いの場合、プロジェクトルート配下に `env.d.ts` を定義すると `process.env` に補完が効いて便利です。

```ts:env.d.ts
// 1. プロジェクトのルートディレクトリに
//    env.d.ts ファイルを作成
// 2. コピペで貼り付け
declare module 'process' {
	global {
		namespace NodeJS {
			interface ProcessEnv {
				readonly NODE_ENV?: "development" | "production"
                readonly BACKEND_URL?: string
                readonly DATABASE_URL?: string
           }
	    }
    }
}
```


### <font color="#5f9ea0">◯ なんで動くの？</font>

上記の `env.d.ts` の定義で下記の `process.d.ts` の定義を **上書き** しているから

```process.d.ts
declare module "process" {
    
    // 中略

    global {
    
        // NodeJS.Process は
        // NodeJS という namespace の中の Process という型
        var process: NodeJS.Process;

        namespace NodeJS {
            
            // 中略

            // env.d.ts は
            // ここの部分を上書きして定義をしている
            interface ProcessEnv extends Dict<string> {
                /**
                 * Can be used to change the default timezone at runtime
                 */
                TZ?: string;
            }

            // 中略

            // NodeJS.Process
            interface Process extends EventEmitter { 

                // 中略

                // env プロパティ
                env: ProcessEnv;

                // 中略

            }
        }
    }
    
    export = process;
}
```

* [DefinitelyTyped/types/node/process.d.ts#L138-L143 - GitHub](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cffaa469e82bae0d617baf24df0a354894f1b4d7/types/node/process.d.ts#L138-L143)

<br />

以下のページで教わりました。誠にありがとうございます。

* [process.envの型定義方法 - Zenn](https://zenn.dev/ken7253/articles/env-variable-type-definition)



### <font color="#5f9ea0">(1) env.d.ts ファイル</font>

`.d.ts` 拡張子のついたファイルを読み込んでくれるそうです。

> コンパイラは適切なパスに従って、.ts、.tsxを見つけようとし、次に.d.tsを探します。 もし特定のファイルが見つからなかった場合、コンパイラはambientモジュール宣言を探し、 .d.tsファイル内にある必要な宣言を呼び戻します。
> [名前空間(namespace)とモジュール - js STUDIO](https://js.studio-kingdom.com/typescript/handbook/namespaces_and_modules)

https://x.com/suin/status/1786427880920273076




### <font color="#5f9ea0">(2) interface</font>

`interface` を使えば型を **上書き** できます。`type` ではできません。

```ts
interface Person {
  name: string
}

interface Person {
  email: string
}

// name と email 両方必要になる
const john: Person = {
  name: "john",
  email: "hoge@example.com"
}
```

* [TypeScript で type と interface どっち使うのか問題 - Zenn](https://zenn.dev/seya/articles/aa94166c977280)
* [You Should Be Using Types Instead Of Interfaces In TypeScript - YouTube](https://youtube.com/shorts/jJGzYdS4ZfY)


### <font color="#5f9ea0">(3) declare module</font>

`process` を (a) と (b) の **２箇所で定義** しています。

```process.d.ts
// (a) モジュールの `process` の型を定義をしている
declare module "process" {

    // (b) グローバルスコープの `process` の型を定義している
    global {
        var process: NodeJS.Process;
    }

    // (b) で定義した process を (a) に export している
    export = process;
}
```

<br />


`declare module` は `import` したり `require` したりして得られたモジュールに型をつけるためのものだと思っています。

なんでグローバルスコープの `process` だけでなくモジュールの `process` の型を定義しないといけないの？🤔という話なのですが `process` は以下のように明示的に `require` できるモジュールだからだと思っています。

```js
const nodeProcess = require('node:process')
nodeProcess === process
// true
```




### <font color="#5f9ea0">(4) declare global (global)</font>

`declare global` はグローバルスコープにあるオブジェクトに型をつけられるものだと思っています。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Remember folks, declaring globals isn&#39;t that hard. <a href="https://t.co/st1ZJfFRMk">pic.twitter.com/st1ZJfFRMk</a></p>&mdash; Matt Pocock (@mattpocockuk) <a href="https://twitter.com/mattpocockuk/status/1745049856207368320?ref_src=twsrc%5Etfw">January 10, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<br />

なんで `declare global` じゃなくて `global` なんだろう？🤔と思ったら二重でつけるのは禁止されているケースもあるそうです。

> いくつかのケースでは、二重にdeclareをつけること自体が禁止されています。
> ```ts
> export declare namespace foo {
>     // 自動的にアンビエントになる (declareを二重につけることはできない)
>     export const x: number;
> }
> ```

* [TypeScriptのdeclareやinterface Windowを勘で書くのをやめる2022 - Zenn](https://zenn.dev/qnighy/articles/9c4ce0f1b68350#%E3%82%A2%E3%83%B3%E3%83%93%E3%82%A8%E3%83%B3%E3%83%88%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88)


<br />

ものによっては `declare global` がついていないコードも見かけますが...

> ```ts
> interface Window {
>   dataLayer: object[];
> }
> 
> window.dataLayer.push({ event: "event_name" });
> ```
* [【初学者向け】具体例で学ぶTypeScript練習問題集 - Zenn](https://zenn.dev/kagan/articles/typescript-practice#%E3%80%90lv.3%E3%80%91window-%E3%82%92%E6%8B%A1%E5%BC%B5%E3%81%99%E3%82%8B)

<br />

あまりよろしくなさそうです...

> `window` にメンバを増やすために以下のように書いている事例がたまにあります。
> ```ts
> interface Window {
>   myGlobalVariable: number;
> }
> ```
> まず大前提として、これはスクリプトファイルか `declare global` 内でしか動作しません。 (さもなくば単に新しい `Window` というインターフェースが定義されるだけ)


* [TypeScriptのdeclareやinterface Windowを勘で書くのをやめる2022 - Zenn](https://zenn.dev/qnighy/articles/9c4ce0f1b68350#interface-window)

<!--

`typescript/lib/lib.dom.d.ts` が GitHub のどこにあるのか現在捜索中...

```typescript/lib/lib.dom.d.ts
declare var window: Window & typeof globalThis;
```
* [漆ノ型：親切包囲網 - Zenn](https://zenn.dev/tkdn/books/type-breathing/viewer/7-courtesy-mind)


-->

### <font color="#5f9ea0">(5) namespace NodeJS</font>

なんでわざわざ NodeJS という namespace を設けたんだろう？🤔と思ったのですが、global スコープを整理するためなんだろうなと思っています。

* [令和5年に知っているべき TypeScript の namespace の知識 - Zenn](https://zenn.dev/uhyo/articles/ts-namespace-2023)


### <font color="#5f9ea0">(6) export = process;</font>

`export = process;` は `module.exports = process;` に近い機能のようです。

> 通常の export default 構文と異なり、export = は CommonJS スタイルの module.exports のように、モジュール全体をその変数 process として直接エクスポートすることを示しています。

* 上記は ChatGPT 調べ

















### <font color="#5f9ea0">◯ そのほか</font>
まだ正直よくわかっていない...

* [ProcessEnv | typescript - v3.7.7](https://microsoft.github.io/PowerBI-JavaScript/interfaces/_node_modules__types_node_process_d_._process_.__global.nodejs.processenv.html)




## 8. おわりに

↓ NODE_ENV の取り扱いの話をされています。

https://zenn.dev/dove/articles/5fd7926e7da949







<!--
https://qiita.com/akameco/items/6567ccb1fd3b2e787f56


### <font color="#5f9ea0">4.1.</font>

* [process.env の型定義方法 - Zenn]()

### <font color="#5f9ea0">4.2. わからないから調べる...</font>

ChatGPT に聞くと `namespace` は削除しても動くらしいです...

```
以下のように書いても良いの？

declare module 'process' {
  global {
    interface ProcessEnv {
      readonly NODE_ENV?: "development" | "production"
      readonly BACKEND_URL?: string
      readonly DATABASE_URL?: string
    }
  }
}
```

> はい、提案された形式でコードを書くこともできます。ただし、この場合、ProcessEnv インターフェイスは NodeJS 名前空間の外に配置されます。これは技術的には問題ありませんが、NodeJS 名前空間を使用することで、Node.js 環境に特有の設定や型を明確に区別することができます。
>
> 名前空間を使用しない場合、ProcessEnv がグローバルな名前空間に含まれるため、他のコンテキストやライブラリとの競合の可能性があります。したがって、コードの可読性とメンテナンスの容易さを考慮して、適切な名前空間を利用することが一般的に推奨されます。

なにを言ってるんだお前...状態...

`global.process.env` の階層ならわかるのですが `process > global > NodeJS` の順になっていてめちゃくちゃでよくわからない...

![o0524034514728490869.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254825/7fca08b3-41d1-561b-670c-7aedb14058fe.jpeg)





## 4. 環境変数を使う理由

開発環境のパソコンでは `development` を指定して、商用環境のサーバでは `production` を指定しておけばデプロイするたびにコードを切り替える必要がなくなる。

手作業でこの切り替えを行うと忘れが発生して開発時のセキュリティ的にヤバいコードをデプロイしてしまう可能性があるので、このようにして環境変数を利用するとどこかで聞いた。

-->