👨‍⚕️

React Native開発:EAS BuildによるmicroCMSリクエストのトラブルシューティング

2023/07/06に公開
1

内容ざっくり

React Nativeで作成したアプリを配布可能なアプリケーション(apk、ipa)に変換する際に遭遇した問題と、それを解決した経緯を解説しています。

具体的には、Expo Application Services(EAS)のbuildコマンドを用いた際に出現した'404エラー'の原因と、その解決策が書かれています。

作ったアプリ

https://zenn.dev/chot/articles/537c99ea098add

起ったこと

作ったアプリを手元だけでなく、配布可能なアプリケーション(apk、ipa)を作成するため、Expo Application Services(EAS)のbuildコマンドをしてみた。

eas build --profile preview

立ち上げたアプリ画面から以下のように出力

Request failed with status code 404

外部にリクエストが必要なものといえば思い当たるのはmicroCMSなので原因を考えてみる。

EAS Buildとは

EAS BuildはExpoとReact Nativeプロジェクト向けのクラウドベースのビルドサービスです。

デフォルト設定により、すぐに配布可能なアプリのビルドを簡素化し、署名資格情報の管理を自動化します。内部配布(internal-distribution)により、ビルドの共有が容易になり、EAS Submitとの密接な連携でアプリストアへの提出もスムーズらしい。

https://docs.expo.dev/build/introduction/

原因の候補

microCMSへのリクエストが404エラーを返すという事象は、通常、以下のいずれかの問題が原因となる可能性があります。

  • リクエスト先のURLが間違っている
  • エンドポイントが存在しない
  • リソースが存在しない

上記はローカル開発環境で検証できるのでおそらく 「アクセス権限がない」 ことが原因として調べてみました。

原因

アクセス権限がない

microCMSでは、APIリクエストにAPIキーを必要とします。APIキーが正しいことはローカル開発環境で検証できているので、リクエスト時にAPIキーを付与できていない線で考えてみます。

まずは公式を見てみます。

https://docs.expo.dev/build-reference/variables/

"Because your EAS Build job runs on a remote server, these .env files might not be available. For instance, .env files are excluded from your uploaded project if they are listed in .gitignore or not committed to your local version control system."

上記は、EAS Buildのジョブがリモートサーバー上で動作するため、.envが利用できない可能性があることを示しています。さらに、.gitignoreにリストされているか、ローカルのバージョン管理システムにコミットされていない場合、.envはアップロードされたプロジェクトから除外されると説明しています。

自分はまさに、.gitignore.envをリストしていたのでこれが原因の可能性が高そうですね。

ローカル環境では404にならなかったのに、配布用にEAS Buildしたら 404になった理由も、リモートビルド環境がローカル開発環境と異なり、APIキーにアクセスができなかったのでしょう。

解決策

同じ公式に解決策になる記述がありました。

"Therefore, EAS Build lets you set per-build-profile environment variables within eas.json as well as sensitive values that should not be committed to source control via EAS Secrets."

https://docs.expo.dev/build-reference/variables/

EAS Buildでは、以下の二つの重要な機能が提供されています

  • eas.jsonの環境変数設定
    • ビルドプロファイルごとの環境変数をeas.json内で設定することが可能
  • EAS Secretsの機密情報設定
    • ソースコード管理にコミットすべきでない機密情報をEAS Secretsを通じて設定することが可能

ローカルの.envファイルを直接EAS Buildで使用することはできず、代わりにEAS Buildで必要な環境変数をeas.jsonやEAS Secretsで設定することが推奨されていると解釈できます。

ただし、これは解釈であり、公式ドキュメントで直接指示されているわけではありません。

やってみる

EAS Secretsを設定

Expoの管理ダッシュボードを使用して、EAS Secretsを設定できます。これはプライベートな情報を保存するための場所で、これらの値はビルド中に利用できます。以下の手順でEAS Secretsを設定できます

  • Expoの管理ダッシュボードにログイン
  • ダッシュボード上でプロジェクトを選択
  • 左側のメニューから「Secrets」を選択
  • 「Create」ボタンをクリック
  • Secretの名前(例:MICROCMS_API_KEY)と値(APIキーの実際の値)を入力

結果

eas.jsonで環境変数を設定

ビルド設定ファイル(eas.json)内で、各ビルドプロファイルに環境変数を定義できます。

eas.json
{
  "cli": {
    "version": ">= 3.14.0"
  },
  "build": {
    "default": {
      "env": {
        "MICROCMS_API_KEY": "@secret:MICROCMS_API_KEY",
        "MICROCMS_SERVICE_DOMAIN": "@secret:MICROCMS_SERVICE_DOMAIN"
      }
    },
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal"
    },
    "production": {
      "env": {
        "MICROCMS_API_KEY": "@secret:MICROCMS_API_KEY",
        "MICROCMS_SERVICE_DOMAIN": "@secret:MICROCMS_SERVICE_DOMAIN"
      }
    }
  },
  "submit": {
    "production": {}
  }
}

環境変数の値をビルドプロファイルごとに異なるものにしたい場合は、それぞれのビルドプロファイルで異なるEAS Secretsを参照するように設定することも可能です。例えば開発用のAPIキーと本番用のAPIキーを分けるとか。

ランタイム上で環境変数を参照する

EAS Buildにおいて環境変数を設定するためには、eas.jsonまたはEAS Secretsにて設定を行いますが、それらの環境変数をアプリ内で使用するためにはapp.config.tsファイル内でextraフィールドに環境変数を定義する必要があります。

https://docs.expo.dev/workflow/configuration/

app.config.ts
import { ExpoConfig, ConfigContext } from "@expo/config";
import { config } from "dotenv";

if (process.env.NODE_ENV !== "production") {
  config(); // ".env" file is loaded only in non-production environments
}

export default ({ config: expoConfig }: ConfigContext): ExpoConfig => {
  return {
    ...expoConfig,
    name: "my-first-react-native-app",
    slug: "my-first-react-native-app",
    extra: {
      microcmsApiKey: process.env.MICROCMS_API_KEY,
      microcmsServiceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
      eas: {
        projectId: "xxxxxxxxxxxxxxxxxx",
      },
    },
  };
};

EAS Buildではdotenvconfig()関数による.envファイルの読み込みは行われません。したがって、ローカル開発環境でのみ.envファイルを読み込むためにdotenvconfig()を使用し、EAS Buildではeas.jsonやEAS Secretsから環境変数を取得するような設定にすることが推奨されます。

例えば下記の記述

src/lib/api.ts
import axios, { AxiosInstance } from "axios";
import { MICROCMS_API_KEY, MICROCMS_SERVICE_DOMAIN } from "@env";

const client: AxiosInstance = axios.create({
  baseURL: `https://${MICROCMS_SERVICE_DOMAIN}.microcms.io/api/v1`,
  headers: { "X-API-KEY": MICROCMS_API_KEY },
});

export const fetchPosts = async () => {
  try {
    const response = await client.get("/news");
    return response.data.contents;
  } catch (error: unknown) {
    console.error(error);
    throw error;
  }
};

export const fetchPostById = async (id: string) => {
  try {
    const response = await client.get(`/news/${id}`);
    return response.data;
  } catch (error: unknown) {
    console.error(error);
    throw error;
  }
};

このコード例で、MICROCMS_API_KEYMICROCMS_SERVICE_DOMAIN@envからインポートされています。@envは、環境変数を一元的に管理するためのモジュールで、環境変数の値はどの環境であっても同じ方法でアクセスすることができます。

ローカル開発環境では.envの内容が@envによってインポートされます。

本番環境 (EAS Build)ではeas.jsonやEAS Secretsから環境変数が読み込まれ、app.config.tsextraフィールドで定義されます。その後、@envを通じてこれらの値にアクセスすることができます。

ただし、@envモジュールは、標準的なNode.jsやReact Nativeのモジュールではないため、プロジェクトでこれを使うには追加の設定が必要です。

babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["babel-preset-expo"],
    plugins: [
      [
        "module:react-native-dotenv",
        {
          moduleName: "@env",
          path: ".env",
        },
      ],
    ],
  };
};

これによりreact-native-dotenvというBabelプラグインが設定され、これにより、@envというモジュール名で環境変数をインポートできます。

この設定でpathに指定された.envから環境変数を読み込み、変数をインポートできるようになります。これにより、.envに定義された環境変数をアプリケーションの中で利用することができます。

import { MICROCMS_API_KEY, MICROCMS_SERVICE_DOMAIN } from "@env";

繰り返しになりますが、これはローカル開発環境での設定であり、本番環境(EAS Build)では代わりにapp.config.tsextraフィールドで環境変数を定義しています。これらの値はeas.jsonやEAS Secretsから読み込まれます。

以上の手順により、EAS Build時にmicroCMSへのリクエストが404エラーを返す問題を解決することができました。

コミット

https://github.com/ryosuketter/my-first-react-native-app/commit/0df036df5f1e9d29ac078b0f60364e95018c3b9b

感想

公式に全部書いてあった。

Discussion

打田裕馬打田裕馬

非常に読みやすく理解しやすかったので、助かりました!
ありがとうございます👏