Nuxt.jsでmicroCMSのAPIキーを隠蔽する

6 min read読了の目安(約5700字

Nuxt.js+microCMSでJamstac構成の静的サイトを作成する際に、APIキーを隠蔽する方法の覚書です。以下のような内容です(インストールや導入部分も簡単に触れていますが適宜読み飛ばしてください)。

  • Nuxt.jsのインストール
  • microCMSの導入とコンテンツの作成
  • コンテンツの表示
  • APIキーの隠蔽

Nuxt.jsのインストール

create-nuxt-appでNuxt.jsをインストールします。今回は以下の設定にしています。

$ npx create-nuxt-app microcms-test

create-nuxt-app v3.6.0
✨  Generating Nuxt.js project in microcms-test
? Project name: microcms-test
? Programming language: JavaScript
? Package manager: Npm
? UI framework: Tailwind CSS
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: ESLint, Prettier
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Static (Static/Jamstack hosting)
? Development tools: jsconfig.json (Recommended for VS Code if you're not using typescript)
? Continuous integration: None
? Version control system: Git
  • モジュールは非同期通信でAPIを利用するためAxiosを選択
  • レンダリングモードはUniversal (SSR / SSG)を選択
  • デプロイのターゲットはStatic (Static/Jamstack hosting)を選択

それ以外は適宜必要なものを選択します。

生成されたpackage.jsonは以下のようになっています。今回Nuxt.jsのバージョンは"2.15.3"を使用しました。

package.json
{
  "name": "microcms-test",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
    "lint": "npm run lint:js"
  },
  "dependencies": {
    "@nuxtjs/axios": "^5.13.1",
    "core-js": "^3.9.1",
    "nuxt": "^2.15.3"
  },
  "devDependencies": {
  //  ...
  }
}

インストールが完了したら、ディレクトリに移動してNuxt.jsを起動します。

cd microcms-test
npm run dev

http://localhost:3000/ でアクセスして、以下のように表示されれば完了です。

microCMSの導入

公式ドキュメントの操作マニュアルの手順にそってmicroCMSを導入します。

https://document.microcms.io/manual/getting-started
アカウント登録 → ログイン → サービスの作成まで行うと、管理画面へのURLが発行されるのでこれにアクセスします。

APIの作成

管理画面にアクセスするとAPIの作成画面に移るので、API名とエンドポイントを設定します。今回はAPIの型にリスト形式を選択して、APIスキーマを以下のように定義しました。

コンテンツの作成

コンテンツはサンプル用に以下のように登録しました。

コンテンツの表示

ここまでできたらNuxt.js側からコンテンツを取得して表示してみます。pages/index.vueを以下のように書き換えます。your-service-idyour-api-keyはそれぞれ作成したサービスのものにします。

pages/index.vue
<template>
  <div>
    <div
      v-for="item in items"
      :key="item.id"
      class="m-4 border-2 border-blue-300"
    >
      <h1 class="p-2 text-xl text-blue-700 bg-blue-300">{{ item.title }}</h1>
      <p class="p-2 text-gray-800">{{ item.body }}</p>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  async asyncData() {
    const { data } = await axios.get('https://your-service-id.microcms.io/api/v1/test', {
      headers: { 'X-API-KEY': 'your-api-key' },
    })
    return {
      items: data.contents,
    }
  },
}
</script>

axiosを利用してasyncData()の戻り値itemsにデータを取得して、それをv-forでリストレンダリングしています。(クラスは適当につけていますが無視してください)
asyncData()はサーバーから非同期データを直接取得しています。

https://ja.nuxtjs.org/docs/2.x/features/data-fetching/#async-data

これでmicroCMSから取得したデータがブラウザに表示されました!

APIキーの隠蔽

ここから本題のAPIキーの隠蔽についてです。
Nuxt.jsの2.12以前では、環境変数としてAPIキーを定義してprocess.env経由で参照する方法や、nuxt.config.js内でenvを利用して設定する方法がありました。process.env経由で利用する方法ではビルド時に定数に変換されてバンドル後のファイルにはAPIキーの値が含まれてしまっている、nuxt.config.js内でenvの設定値はクライアント側から参照できてしまう、などそれぞれ問題がありました。Nuxt.jsの2.13以降からはnuxt.config.jspublicRuntimeConfigprivateRuntimeConfigプロパティを利用してAPIキーをクライアント側から完全に隠蔽できるようになりました。

環境変数を定義して読み込む

まず、ルート階層に.envファイルを作成し、以下のように記述します。
your-service-idyour-api-keyはそれぞれ作成したサービスのものに置き換えます。

.env
BASE_URL=http://localhost:3000
API_KEY=your-api-key
API_URL=https://your-service-id.microcms.io/api/v1

.envを.gitignoreに追記してのバージョン管理から除外しておきます(create-next-appでインストールしていればデフォルトで除外されていると思われます)

次にNuxt.jsからこれを参照するためにnuxt.config.jsに以下のように定義します。

nuxt.config.js
const { API_KEY, API_URL } = process.env;
export default: {
  // ...
  publicRuntimeConfig: {
    apiUrl: API_URL,
    apiKey: process.env.NODE_ENV !== 'production' ? API_KEY : undefined
  },
  privateRuntimeConfig: {
    apiKey: API_KEY,
  },
}

publicRuntimeConfigにはクライアント側で利用するための環境変数を定義し、privateRuntimeConfigにはサーバー側で利用するための環境変数を定義します。

publicRuntimeConfig内ではprocess.env.NODE_ENVproductionでない場合(開発中npm run dev時)にAPI_KEYの値を参照できるようにして、本番環境では参照しないようにしています。
publicRuntimeConfigの値はprivateRuntimeConfigによって上書きされるので、本番環境ではクライアント側からは隠蔽されます。

https://ja.nuxtjs.org/docs/2.x/directory-structure/nuxt-config#runtimeconfig

APIキーの記述を書き換える

これで$configから情報を取得できるようになったので、asyncData()の引数に$configを渡して$config.apiKeyでAPIキーを参照するようにpages/index.vueを書き換えます。
(urlも$config.apiUrlで参照するように書き換えています)

pages/index.vue
<template>
  <div>
    <div
      v-for="item in items"
      :key="item.id"
      class="m-4 border-2 border-blue-300"
    >
      <h1 class="p-2 text-xl text-blue-700 bg-blue-300">{{ item.title }}</h1>
      <p class="p-2 text-gray-800">{{ item.body }}</p>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  async asyncData({ $config }) {
    const { data } = await axios.get(`${$config.apiUrl}/test`, {
      headers: { 'X-API-KEY': $config.apiKey },
    })
    return {
      items: data.contents,
    }
  },
}
</script>

これでAPIキーを隠蔽しつつmicroCMSから取得したデータをブラウザに表示できるようになりました。

参考