Next.jsにて環境変数を用いて、APIのURLを変更する方法

## 概要
SPA構成のシステムを開発する際に、本番環境や開発環境によってAPIのURLが変わるので、それを環境ごとに切り替えたい！ってことよくありますよね。
こういう場合は環境変数を用いて使用するURLを切り替えるのがオーソドックスなのですが、Next.jsだと環境変数の取り扱いが少し変わってくるので、それを今回まとめてみました！

## やりたいこと
以下の3種類の環境のAPIを用意しています。

```
1. 本番環境用のAPI (production)
2. 開発環境用のAPI (development)
3. 開発環境用のモックAPI (swagger)
```

3つ目のモックAPIはOpenAPI(swagger)と呼ばれるもので、スキーマを用いてAPIを定義するだけで擬似的にAPIを使用することができます。
サーバーサイドのAPIの開発を待ってからフロントのAPI関連の実装をすると効率が悪いので、swaggerを用いてモックAPIを作成し、先行してフロントの開発を実行することができます。

Open APIについては以下の記事が分かりやすいのでご覧いただければと思います。

- [本当に使ってよかったOpenAPI (Swagger) ツール](https://future-architect.github.io/articles/20191008/)
- [Swaggerの概要をまとめてみた。](https://qiita.com/gcyata/items/342073fa7607fd4082bd)

よって、上記3つのAPIのURLを環境ごとに切り替えていく必要があります。

## Nuxt.jsの場合
Next.jsの方法の前にオーソドックスのやり方として、まずNuxt.jsにおける環境ごとの切り替え方法を説明します。

まずは以下の3つのenvファイルにそれぞれの環境のAPIのURLを記載します。

```env.production.js
module.exports = {
  // APIのURL
  apiBaseUrl: 'https://prodaution.example.com'
}
```

```env.development.js
module.exports = {
  // APIのURL
  apiBaseUrl: 'http://localhost:80'
}
```

```env.swagger.js
module.exports = {
  // APIのURL
  apiBaseUrl: 'http://localhost:4010'
}
```

そしてこのenvファイルをnuxt.config.jsにて呼び出して、「env」に設定します。
これによってNuxtプロジェクト全体で「process.env.apiBaseUrl」の記載でAPIのURLを呼び出すことができます。
取得したAPIのURLをaxiosに設定することでAPI通信ができるようになるので結構便利です！

```nuxt.config.js
// 1. NODE_ENVから環境変数を呼び出す
const environment = process.env.NODE_ENV || development
// 2. envファイルを呼び出す
const envSet = require(`./env.${environment}.js`)


export default {
  ・・・
  // 3. envファイルを設定
  env: envSet,

}
```

「NODE_ENV」から値を取得していますが、これはどこから取得しているのでしょう？
これは「yarn dev」や「yarn start」などでNuxtを実行した際に取得しています。

package.jsonに以下のように記載します。

```package.json
"script": {
  "build": "NODE_ENV=production nuxt build", // 本番環境
  "dev" : "NODE_ENV=development nuxt", // 開発環境
  "dev:swagger" : "NODE_ENV=swagger nuxt" // 開発環境 モックAPI
}
```

この設定によって「yarn dev」コマンドを実行した際に、"NODE_ENV=development nuxt"が実行されるので、NODE_ENVに"development"が与えられ、nuxt.confing.jsで読み込まれるという仕組みです。

Open API(swagger)のモックAPIを使用したい場合は「yarn dev:swagger」のコマンドを実行し、NODE_ENVから"swagger"を取得して、nuxt.config.jsonの「env」に「env.swagger.js」の値を設定するようにします。

これでコマンドだけで簡単に各環境のAPIを使用できるようになりました。

## Next.jsの場合

それではNext.jsの場合も同様にやってみましょう！
まずNuxt.jsと同じようにenvファイルを作成しましょう。
Next.jsの場合は「.js」のような拡張子はいらず、「.env.production」のようなファイル名にします。

```　.env.production
// 本番環境APIのURL
API_BASE_URL=https://prodaution.example.com
```

```　.env.development
// 開発環境APIのURL
API_BASE_URL=http://localhost:80
```

```　.env.swagger
// モックAPIのURL
API_BASE_URL=http://localhost:4010
```

### Next.jsにおけるNODE_ENVの取り扱い
ここがNext.jsで環境変数を使用する際のハマりポイントです。。
Nuxt.jsとは異なり、**コマンドにNODE_ENVを仕込むことは非推奨**とされています。

試しに"NODE_ENV=swagger next"のようなコマンドを作成し実行すると、consoleに以下のようなwarningが表示されます。

```
You are using a non-standard "NODE_ENV" value in your environment. This creates inconsistencies in the project and is strongly advised against. Read more: https://err.sh/next.js/non-standard-node-env
```

またこちらの記事でも記載の通り、**Next.jsはコマンドごとにNODE_ENVに設定される値が決められています。**

- [【Next.js】環境変数(.env)の使い方【公式ドキュメント 意訳】](https://mmll.hatenablog.com/entry/category/tools/next-js/next-js-env-usage)
- [Test Environment Variables](https://nextjs.org/docs/basic-features/environment-variables#test-environment-variables)

```
// NODE_ENVの値
- 「next dev」コマンドでは、developmentの値が設定される
- 「next build」、「next start」コマンドでは、productionの値が設定される
- 「jest」実行時は、testの値が設定される
```

また、NODE_ENVの値によって、適用される「.env」ファイルも自動で適用されます。

```
- developmentの場合、「.env.development」または「.env.development.local」
- productionの場合、「.env.production」または「.env.production.local」
- testの場合、「.env.test」または「.env.test」
```

なので、**Next.jsではNuxt.jsのように実行コマンドにNODE_ENVを仕込んで切り替えることができません。**

## Next.jsにてAPIのURLを変更する方法
ではどうするか？ですが、結論をいうと、**「NODE_ENV」以外の環境変数を使用すればいい！**です！
(結構無理やりなやり方ですが。。)

#### 参考
- [How to setup custom environment variables other than development and production](https://github.com/vercel/next.js/discussions/12077)

そのためには「dotenv」というライブラリが必要になるのでinstallしておきます。

```
yarn add dotenv
```

そしてpackage.jsonのscriptコマンドを以下のように記載します。

```package.json
"scripts": {
    "dev": "next",
    "dev:swagger": "ENVIRONMENT=swagger next",
    "build": "next build",
    "start": "next start",
  },
```

モックAPIを呼び出すコマンド「yarn dev:swagger」に、**"ENVIRONMENT=swagger next"**を実行しています。
環境変数に"ENVIRONMENT"を使用しているので、これで**「process.env.ENVIRONMENT」**でコマンドに設定した値を取得することができます。

次にnext.config.jsにenvファイルを取得する設定を記載します。

```next.config.js
// 各環境のenvファイルを読み込む
require('dotenv').config({ path: `./.env.${process.env.ENVIRONMENT}` })

module.exports = {}
```

### 注意点その1
しかし、これでenvファイルの設定値が使用できるようになったわけではありません。

Next.jsではクライアント側で使用したい環境変数には、**頭文字に「NEXT_PUBLIC_」をつける必要があります。**
これを付けないと、サーバー側でしか使用することができなくなります。

- [Exposing Environment Variables to the Browser](https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser)

よって各envファイルの環境変数を以下のように変更します。

```　.env.production
// 本番環境APIのURL
NEXT_PUBLIC_API_BASE_URL=https://prodaution.example.com
```

```　.env.development
// 開発環境APIのURL
NEXT_PUBLIC_API_BASE_URL=http://localhost:80
```

```　.env.swagger
// モックAPIのURL
NEXT_PUBLIC_API_BASE_URL=http://localhost:4010
```

これでクライアント側で**「process.env.NEXT_PUBLIC_API_BASE_URL」**でAPIの値を取得し使用することができます。

※クライアント側で使用できるということは開発者ツールなどでその値を外部に公開する事になります。機密事項など公開しないように注意してください。

### 注意点その２
これでAPIのURLを使えるようになったぞ！と思いますが、最後に1つ落とし穴があります。

それは「.env.development」と「.env.swagger」で使用されているAPIの環境変数が同じ変数名であるため、**モックAPIのコマンドを実行しても、「.env.development」のAPIのurlが反映されてしまう**事です。

なぜこのようなことが起こるのか？
それは実行コマンドを確認すれば理解できます。

実行コマンドは「"dev:swagger": "ENVIRONMENT=swagger next",」となっています。

この実行順は、以下のようになります。

```
1. 「ENVIRONMENT=swagger」でnext.config.jsにて「.env.swagger」を読み込む
2. 「next」で自動的に「.env.development」が適用される
```

つまり、1度「.env.swagger」で読み込まれてから「.env.development」を読み込むため、**同じ変数名のものは　「.env.developent」に記載のものに上書かれてしまいます。**

ではどうすればいいか？

それは**「違う変数名を使用する」**です！(これも結構無理やり。。)

「.env.swagger」の環境変数を以下のように変更します。

```　.env.swagger
// モックAPIのURL
- NEXT_PUBLIC_API_BASE_URL=http://localhost:4010
+ NEXT_PUBLIC_OPEN_API_BASE_URL=http://localhost:4010
+ NEXT_PUBLIC_ENVIRONMENT=swagger
```
こうすることで「.env.development」の値で上書きされないようにします。

また新たに**「NEXT_PUBLIC_ENVIRONMENT」**を追記しています。
これはクライアント側で「NEXT_PUBLIC_OPEN_API_BASE_URL」(モックAPI)を使うか、「NEXT_PUBLIC_API_BASE_URL」(開発環境、本番環境のAPI)を使うかを判断するために使用します。

使用方法は以下のようになります。

```xxx.js
/**
 * API関数取得メソッド
 */
const resolveApiBasePath = () => {
  /// 環境変数「NEXT_PUBLIC_OPEN_API_BASE_URL」が存在する場合、その値を取得
  const env = process.env.NEXT_PUBLIC_ENVIRONMENT
    ? process.env.NEXT_PUBLIC_ENVIRONMENT
    : ''

  switch (env) {
    case 'swagger':
      // "swagger"の場合、swaggerのAPIのURLを使用する
      return process.env.NEXT_PUBLIC_OPEN_API_BASE_URL
    default:
      // development, productionのAPIのURLを使用する
      return process.env.NEXT_PUBLIC_API_BASE_URL
  }
}

```

これでswaggerの時はモックAPIのURLを適用させ、development, productionの場合はそれぞれのAPIのURLを適用させることができます。

※「.env.development」にswaggerと同じ名前の環境変数を記載すると上書かれるので、それぞれ一意の変数名を定義してください。

## 最後に
少し長くなりましたが、以上がNext.jsにて環境変数を用いてAPIのURLを変更する方法の説明になります。

最終盤のソースコードをまとめますので、ご活用いただけると幸いです。

### envファイル

```　.env.production
// 本番環境APIのURL
NEXT_PUBLIC_API_BASE_URL=https://prodaution.example.com
```

```　.env.development
// 開発環境APIのURL
NEXT_PUBLIC_API_BASE_URL=http://localhost:80
```

```　.env.swagger
// モックAPIのURL
NEXT_PUBLIC_OPEN_API_BASE_URL=http://localhost:4010
NEXT_PUBLIC_ENVIRONMENT=swagger
```

### package.json
```package.json
"scripts": {
    "dev": "next",
    "dev:swagger": "ENVIRONMENT=swagger next",
    "build": "next build",
    "start": "next start",
  },
```


### next.config.js

```next.config.js
// 各環境のenvファイルを読み込む
require('dotenv').config({ path: `./.env.${process.env.ENVIRONMENT}` })

module.exports = {}
```

### クライアント側でのAPIURLの取得

```xxx.js
/**
 * API関数取得メソッド
 */
const resolveApiBasePath = () => {
  /// 環境変数「NEXT_PUBLIC_OPEN_API_BASE_URL」が存在する場合、その値を取得
  const env = process.env.NEXT_PUBLIC_ENVIRONMENT
    ? process.env.NEXT_PUBLIC_ENVIRONMENT
    : ''


  switch (env) {
    case 'swagger':
      // "swagger"の場合、swaggerのAPIのURLを使用する
      return process.env.NEXT_PUBLIC_OPEN_API_BASE_URL
    default:
      // development, productionのAPIのURLを使用する
      return process.env.NEXT_PUBLIC_API_BASE_URL
  }
}

```